home *** CD-ROM | disk | FTP | other *** search
- {I made this game as a challenge. It took me two-and-half hours to finish it,
- starting from scratch.
- To compile this code you'll need two units:
- - SMGRAF for some graphics routines (initgraf, setpal & fades).
- - SMFONT30 for the font.
- You can download both from my website at http://www.xs4all.nl/~remcodek/
- (go to the fontpage for the font and the downloadpage for SMGRAF).
- You may alter and/or distribute this code and program but I would appreciate
- it if you'd keep me informed and gave me some credit(s). Handing it in as
- homework is very unwise.
-
- I added some comments hoping to make it all a bit clearer. I'm not used to
- doing that so if you still find it cryptic I'm sorry.
-
- If you need some comments or help let me know.
- Have fun!
-
- Remco de Korte
- e-mail: remcodek@xs4all.nl}
-
- program tiktak;
-
- uses
- crt,graph,smgraf,smfont30;
-
- type
- gridtype=array[0..2,0..2] of byte;
-
- var
- p:array[0..8] of pointer; {tilting tiles}
- grid,checktable:gridtype; {store the grid to check}
- back:pointer; {to (re)store part of the screen}
-
- const
- fontcol:tctype30=(8,9,10,11,12,13,14,15,15,15,15,15,15,15,15,15);
- fontgray:tctype30=(1,2,3,4,5,6,7,7,7,7,7,7,7,7,7,7);
- {constants for the colors of the font}
-
- procedure preparegraphics;
-
- var
- i,j,k,z:integer;
-
- begin
- initgraf; {initialize graphics - same as GRAPHs InitGraph()}
- getmem(back,imagesize(0,0,79,79)); {allocate memory for the size of a tile}
- for i:=0 to 7 do setpal(colnum[i],i*5,i*5,i*5);
- for i:=0 to 7 do setpal(colnum[8+i],42+i*3,21+i*6,i*6); {initialize custom palette}
- quickfadeout(1); {fade to black}
- for j:=0 to 79 do for i:=0 to 79 do putpixel(i,j,1+random(5)); {draw 'raw' tile...}
- setcolor(7);
- rectangle(1,1,79,79);
- setcolor(0);
- rectangle(0,0,79,79); {...with some edges...}
- setfillstyle(1,0);
- bar(5,5,8,8);
- bar(72,5,75,8);
- bar(5,72,8,75);
- bar(72,72,75,75);
- setfillstyle(1,7);
- bar(5,5,6,6);
- bar(72,5,73,6);
- bar(5,72,6,73);
- bar(72,72,73,73); {...and some bolts}
- for i:=0 to 79 do for j:=0 to 79 do
- begin
- k:=getpixel(i,j)+getpixel((i+1) mod 80,j)+getpixel(i,(j+1) mod 80)+getpixel((i+1) mod 80,(j+1) mod 80);
- putpixel(i,j,k div 4); {smoothen tile}
- end;
- getmem(p[0],imagesize(0,0,79,79)); {allocate memory for full tile}
- getimage(0,0,79,79,p[0]^); {store tile}
- setfillstyle(1,0);
- for z:=1 to 8 do
- begin
- k:=trunc(40*cos(z*pi/16)); {calculate height of tilting tile}
- bar(80,0,159,79); {clear background}
- if k>0 then for j:=0 to k do for i:=0 to 79 do
- begin
- putpixel(i+80,39-j,getpixel(i,39-j*40 div k)); {draw top half of tile}
- putpixel(i+80,40+j,getpixel(i,40+j*40 div k)); {draw bottom half}
- end;
- getmem(p[z],imagesize(0,0,79,79)); {allocate memory}
- getimage(80,0,159,79,p[z]^); {store tilted tile}
- end;
-
- for j:=0 to 5 do for i:=0 to 7 do putimage(i*80,j*80,p[0]^,0); {fill the screen with tiles}
- quickfadein(256); {fade in, slowly, ironically}
- for j:=1 to 4 do for i:=2 to 5 do for k:=1 to 8 do {remove center with a simple animation}
- begin
- putimage(i*80,j*80,p[k]^,0);
- delay(50);
- end;
- for j:=2 downto 0 do for i:=2 downto 0 do for k:=7 downto 0 do {draw tiles of playing field}
- begin
- putimage(i*80+200,j*80+120,p[k]^,0);
- delay(50);
- end;
- font30_4('Tik!',200,90,2,2,fontcol); {draw text}
- delay(1000);
- font30_4('Tak!',280,90,2,2,fontcol);
- delay(1000);
- font30_4('Tor!',360,90,2,2,fontcol);
- font30_2('Use arrows to move,',220,364,1,2,fontgray);
- font30_2('Enter to select',248,380,1,2,fontgray);
- end;
-
- procedure quit(msg:string;x:integer);
-
- begin
- setfillstyle(0,0);
- bar(160,360,479,399); {remove previous text}
- repeat
- font30_4(msg,x,368,4,1,fontgray); {draw text in gray}
- if not keypressed then delay(500);
- font30_4(msg,x,368,4,1,fontcol); {draw text in color}
- if not keypressed then delay(500);
- until keypressed; {repeating makes the text blink}
- emptykey; {empty the keyboardbuffer}
- closegraph;
- halt; {the end......}
- end;
-
- procedure plot(x,y:integer); {just a fancy way to draw}
-
- var
- z,xx,yy,kk:integer;
-
- begin
- for z:=0 to 15 do {randomly distribute some pixels around a given pixel}
- begin
- xx:=random(z)-z div 2;
- yy:=random(z)-z div 2;
- kk:=15-(abs(xx)+abs(yy)) div 2; {the closer the pixel is to the center, the lighter the color}
- if getpixel(x+xx,y+yy)<8 then putpixel(x+xx,y+yy,kk);
- end;
- for z:=0 to 15 do {increase the 'pixel-density' towards the center}
- begin
- xx:=random(15)-7;
- yy:=random(15)-7;
- kk:=15-(abs(xx)+abs(yy)) div 2;
- if getpixel(x+xx,y+yy)<8 then putpixel(x+xx,y+yy,kk);
- delay(1); {show you're stuff....}
- end;
- end;
-
- function check(gr:gridtype;cc:byte):byte; {checks whether a game is won by player cc}
-
- var
- eval:byte;
-
- begin
- eval:=0; {there are 8 different ways to win, each one has its code,
- used for drawing}
- if gr[0,0]=cc then if gr[0,1]=cc then if gr[0,2]=cc then eval:=1;
- if gr[0,0]=cc then if gr[1,0]=cc then if gr[2,0]=cc then eval:=2;
- if gr[0,0]=cc then if gr[1,1]=cc then if gr[2,2]=cc then eval:=3;
- if gr[1,0]=cc then if gr[1,1]=cc then if gr[1,2]=cc then eval:=4;
- if gr[0,1]=cc then if gr[1,1]=cc then if gr[2,1]=cc then eval:=5;
- if gr[2,0]=cc then if gr[1,1]=cc then if gr[0,2]=cc then eval:=6;
- if gr[2,0]=cc then if gr[2,1]=cc then if gr[2,2]=cc then eval:=7;
- if gr[0,2]=cc then if gr[1,2]=cc then if gr[2,2]=cc then eval:=8;
- check:=eval; {if check=0 then the game isn't over, otherwise the value
- tells you which line is complete}
- end;
-
- procedure cross(x,y:integer); {draws a colored cross}
-
- var
- i:integer;
-
- begin
- for i:=16 to 63 do plot(200+x*80+i,120+y*80+i);
- for i:=16 to 63 do plot(279+x*80-i,120+y*80+i);
- end;
-
- procedure naught(x,y:integer); {draws a colored circle}
-
- var
- i:integer;
-
- begin
- for i:=0 to 63 do plot(240+x*80+trunc(24*sin(i*pi/32)),160+y*80+trunc(24*cos(i*pi/32)));
- end;
-
- procedure wipe(x,y:integer); {makes cross or circle gray}
-
- var
- i,j:integer;
-
- begin
- for j:=y*80+120 to y*80+199 do for i:=x*80+200 to x*80+279 do
- if getpixel(i,j)>7 then putpixel(i,j,getpixel(i,j)-8); {if a pixel is colored make it gray}
- end;
-
- procedure playermove;
-
- var
- px,py:integer; {position}
- i,j:integer; {dummy- or loop-variables}
-
- begin
- font30_2('Use arrows to move,',220,364,1,2,fontcol); {highlight text}
- font30_2('Enter to select',248,380,1,2,fontcol);
- px:=0;
- py:=0; {start in the topleft corner}
- repeat
- getimage(200+px*80,120+py*80,279+px*80,199+py*80,back^); {store the screen before drawing}
- setcolor(9);
- rectangle(200+px*80,120+py*80,279+px*80,199+py*80); {draw a semi-3D-rectangle}
- setcolor(8);
- rectangle(202+px*80,122+py*80,279+px*80,199+py*80);
- setcolor(10);
- rectangle(200+px*80,120+py*80,277+px*80,197+py*80);
- setcolor(14);
- rectangle(201+px*80,121+py*80,278+px*80,198+py*80);
- repeat
- getkey;
- until key in [#13,#27,#199..#201,#203,#205,#207..#209]; {wait for a key to be pressed, check whether it's a valid key}
- putimage(200+px*80,120+py*80,back^,0); {restore screen/remove rectangle}
- if key in [#199,#203,#207] then px:=(px+2) mod 3; {the numeric keypad has extended charactres, the}
- if key in [#201,#205,#209] then px:=(px+1) mod 3; {GETKEY-statement from the SMGRAF-unit reads the second}
- if key in [#199..#201] then py:=(py+2) mod 3; {charactre and adds 128 to its ordinal value}
- if key in [#207..#209] then py:=(py+1) mod 3; { - hence these values}
- if key=#13 then if grid[px,py]>0 then key:=#255; {#13 is the Enter-key. If it's pressed over an invalid square
- (not empty) 'key' gets a dummy value}
- until key in [#13,#27]; {#27 is the Esc-key}
- if key=#27 then quit('You gave up...',180); {when Esc is pressed the game is aborted, 'Quit' shows a
- message (msg) at horizontal coordinate 'x'}
- grid[px,py]:=1; {when Enter is pressed the grid-value is set to 1 indicating
- a cross for the human player}
- cross(px,py); {draw the cross}
- delay(1000); {wait}
- wipe(px,py); {'gray out' the cross}
- i:=check(grid,1); {check whether the human player ('1') has won}
- if i>0 then {if so...}
- begin
- if i in [1,2,3] then cross(0,0);
- if i in [2,4] then cross(1,0);
- if i in [2,6,7] then cross(2,0);
- if i in [1,5] then cross(0,1);
- if i in [3,4,5,6] then cross(1,1);
- if i in [5,7] then cross(2,1);
- if i in [1,6,8] then cross(0,2);
- if i in [4,8] then cross(1,2);
- if i in [3,7,8] then cross(2,2); {highlight the winning line, retrieved from the check-value}
-
- quit('You win!',232); {congratulations}
- end;
- end;
-
- procedure computermove;
-
- var
- cx,cy:integer; {position}
- table:array[0..2,0..2] of integer; {stores a value for each field indicating a good or bad move}
- tmpgrid:gridtype; {to store a temporary playing field}
- i,j,x,y,a,b:integer; {dummy- or loop-variables}
-
- begin
- font30_2('Use arrows to move,',220,364,1,2,fontgray); {'gray out' text}
- font30_2('Enter to select',248,380,1,2,fontgray);
- for i:=0 to 2 do for j:=0 to 2 do table[i,j]:=1000+random(10);{set initial value for each move at 1000 and something; the
- highest value indicates the best move}
- inc(table[0,0],8); {give the corners some extra, they're better to start with}
- inc(table[0,2],8);
- inc(table[2,0],8);
- inc(table[2,2],8);
- inc(table[1,1],10); {give the center a big extra, it's best to start with}
- for i:=0 to 2 do for j:=0 to 2 do {start checking each possible move}
- begin
- if grid[i,j]>0 then table[i,j]:=-1 {if a square is not empty set the table-value at -1: invalid}
- else
- begin
- for x:=0 to 2 do for y:=0 to 2 do tmpgrid[x,y]:=grid[x,y];{make a temporary playing field}
- tmpgrid[i,j]:=1; {simulate a human player's move at (i,j)}
- if check(tmpgrid,1)>0 then inc(table[i,j],200); {if it's a winning move increase the table-value to block this
- human move}
- tmpgrid[i,j]:=2; {simulate a computer move at (i,j)}
- if check(tmpgrid,2)>0 then inc(table[i,j],1000); {if it's a winning move for the computer increase a lot: this
- must be the move!}
- for a:=0 to 2 do for b:=0 to 2 do if tmpgrid[a,b]=0 then {check for the human player's move next}
- begin
- for x:=0 to 2 do for y:=0 to 2 do tmpgrid[x,y]:=grid[x,y];
- tmpgrid[i,j]:=2; {reset the temporary grid at this 'virtual move'}
- tmpgrid[a,b]:=1; {add a human's move}
- if check(tmpgrid,1)>0 then dec(table[i,j],50); {if the human will win on this next move the computer should
- find a better one}
- end;
- end;
- end;
- cx:=0;
- cy:=0;
- for i:=0 to 2 do for j:=0 to 2 do if table[i,j]>table[cx,cy] then {find the highest table-value}
- begin
- cx:=i;
- cy:=j;
- end;
- grid[cx,cy]:=2; {put a naught here - '2' for computer-player}
- naught(cx,cy); {draw a circle}
- delay(1000); {wait}
- wipe(cx,cy); {'gray out' the circle}
- i:=check(grid,2); {check if it's a winning move}
- if i>0 then {if so....}
- begin
- setfillstyle(0,0);
- bar(160,360,479,399); {same as in playermove}
- if i in [1,2,3] then naught(0,0);
- if i in [2,4] then naught(1,0);
- if i in [2,6,7] then naught(2,0);
- if i in [1,5] then naught(0,1);
- if i in [3,4,5,6] then naught(1,1);
- if i in [5,7] then naught(2,1);
- if i in [1,6,8] then naught(0,2);
- if i in [4,8] then naught(1,2);
- if i in [3,7,8] then naught(2,2);
- quit('You lose!',232); {rub it in}
- end;
- end;
-
- procedure play;
-
- var
- i,j,k:integer;
-
- begin
- for i:=0 to 2 do for j:=0 to 2 do grid[i,j]:=0; {initialize empty playing field}
- if random(2)=0 then computermove; {toss for the first turn}
- repeat
- playermove;
- k:=0;
- for i:=0 to 2 do for j:=0 to 2 do if grid[i,j]>0 then inc(k);{check if all squares are filled}
- if k<9 then computermove; {if not k<9}
- k:=0;
- for i:=0 to 2 do for j:=0 to 2 do if grid[i,j]>0 then inc(k);
- until k=9; {loop until all squares are filled}
- quit('It''s a draw.',205); {if noone wins... it's a draw}
- end;
-
- begin
- preparegraphics; {obvious}
- play; {obvious}
- end.