home *** CD-ROM | disk | FTP | other *** search
- {$C-}
- {$R-}
- {$U-}
- {$K-}
-
- program reversi;
-
- {
- Program REVERSI by M. Quinlan 5/26/84
- based on a program in the book Advanced Pascal Programming Techniques
- by Paul A. Sand
-
- Version 1 Release 0 Modification level 0
-
- Program from the book modified for the IBM PC and Turbo Pascal.
-
- Version 1 Release 1 Modification level 0
-
- Use all-points-addressable graphics for the board display (loosly based on
- the section in the book titled "Modifying reversi for Graphics").
-
- Version 1 Release 2 Modification level 0
-
- Use customized characters in medium-resolution graphics mode for the
- board display (much faster than APA graphics). On the "human" move,
- show him which discs would be flipped.
-
- Version 1 Release 3 Modification level 0
-
- Allow human vs. human and computer vs. computer.
-
- Version 1 Release 4 Modification level 0
-
- Display version on the screen.
- Change board evaluation routine:
- if end of game detected, base evaluation totally on the score
- make the sides more important
- make "poison2" squares less important
-
- * Version 1 Release 4 Modification level 1
-
- Make minor changes so the input will work correctly with Turbo Pascal
- Version 3.01a and PC DOS 3.1.
-
- ==============================================================================
-
- Possible future enhancements:
-
- Help system on entry to describe rules of game, possible strategy, and
- this implementation (how to make a move, etc.).
-
- Allow human to ask computer for a suggested move.
-
- Allow setup mode where human can put disc of either color an any square.
-
- Allow change of mode or player at any time (i.e. play for a while then let
- the computer take over, etc.).
-
- Better handle arrow keys when human selects move: allow Up, Down, Left, Right
- and have the keys move to the next legal square in that direction.
-
- Improve performance.
-
- Improve the level of play.
-
- }
-
- const
- MAXMOVES = 60;
-
- LIGHT = 0;
- DARK = 1;
- EMPTY = 2;
- BORDER = 3;
-
- type
- contents = LIGHT..BORDER;
- plcolor = LIGHT..DARK;
- pltype = (COMPUTERPLAYER, HUMANPLAYER);
- squarenum = 0..99;
- movelist = record
- nmoves: 0..MAXMOVES;
- move: array [1..MAXMOVES] of squarenum
- end;
- board = record
- sq: array [squarenum] of contents;
- ndiscs: array [plcolor] of integer;
- possible: movelist
- end;
- direction = (NORTH, NORTHEAST, EAST, SOUTHEAST,
- SOUTH, SOUTHWEST, WEST, NORTHWEST);
-
- var
- ch: char;
- delta: array [direction] of integer;
- sqord: array [squarenum] of integer;
- sqchar: array [contents] of char;
- corner, poison1, good1: array [1..4] of squarenum;
- poison2, good2: array [1..4, 1..2] of squarenum;
- edge: array [1..4, 1..4] of squarenum;
-
- { GRAPHDRA.PAS }
- const
- xbase = 0;
- ybase = 4;
- xscale = 2;
- yscale = 2;
- PIXELSPERCHAR = 8;
-
- bgcolor = 0;
- palcolor = 3;
-
- black = 0;
- lcyan = 1;
- lmagenta = 2;
- white = 3;
-
- SQLIGHT = 0;
- SQDARK = 1;
- SQEMPTY = 2;
- SQCURSOR = 3;
- SQTOBELIGHT = 4;
- SQTOBEDARK = 5;
-
- type
- color = black..white;
- sqtype = SQLIGHT..SQTOBEDARK;
-
- type
- chardefarray = array[0..191] of byte;
-
- const
- chardef: chardefarray = ($FF, $80, $80, $9F, $9F, $9F, $9F, $9F,
- $FF, $01, $01, $F9, $F9, $F9, $F9, $F9,
- $9F, $9F, $9F, $9F, $9F, $80, $80, $FF,
- $F9, $F9, $F9, $F9, $F9, $01, $01, $FF,
-
- $FF, $80, $80, $9F, $9F, $98, $98, $98,
- $FF, $01, $01, $F9, $F9, $19, $19, $19,
- $98, $98, $98, $9F, $9F, $80, $80, $FF,
- $19, $19, $19, $F9, $F9, $01, $01, $FF,
-
- $FF, $80, $80, $80, $80, $80, $80, $80,
- $FF, $01, $01, $01, $01, $01, $01, $01,
- $80, $80, $80, $80, $80, $80, $80, $FF,
- $01, $01, $01, $01, $01, $01, $01, $FF,
-
- $FF, $80, $80, $80, $80, $80, $80, $80,
- $FF, $01, $01, $01, $01, $01, $01, $01,
- $80, $80, $80, $80, $80, $80, $80, $FF,
- $01, $01, $01, $01, $01, $01, $01, $FF,
-
- $FF, $80, $80, $9F, $9F, $98, $98, $98,
- $FF, $01, $01, $F9, $F9, $19, $19, $19,
- $98, $98, $98, $9F, $9F, $80, $80, $FF,
- $19, $19, $19, $F9, $F9, $01, $01, $FF,
-
- $FF, $80, $80, $9F, $9F, $9F, $9F, $9F,
- $FF, $01, $01, $F9, $F9, $F9, $F9, $F9,
- $9F, $9F, $9F, $9F, $9F, $80, $80, $FF,
- $F9, $F9, $F9, $F9, $F9, $01, $01, $FF);
-
- procedure initgraph;
- begin
- GraphColorMode;
- GraphBackground(bgcolor);
- Palette(palcolor);
- Textcolor(lcyan);
- MemW[$0000:$007E] := Seg(chardef);
- MemW[$0000:$007C] := Ofs(chardef);
- end;
-
- procedure dispgrid;
- begin { dispgrid }
- end; { dispgrid }
-
- procedure buildsquare;
- begin
- end;
-
- procedure fillbkgrnd(i, j: integer);
- var
- x, y: integer;
- xpscale, ypscale: integer;
- begin
- xpscale := PIXELSPERCHAR * xscale;
- ypscale := PIXELSPERCHAR * yscale;
- x := ((i * xscale) + xbase) * PIXELSPERCHAR;
- y := ((j * yscale) + ybase) * PIXELSPERCHAR;
- draw(x + 1, y + 1, x + xpscale - 1, y + 1, lmagenta);
- draw(x + 1, y + 2, x + xpscale - 1, y + 2, lmagenta);
- draw(x + 1, y + ypscale - 2, x + xpscale - 1, y + ypscale - 2, lmagenta);
- draw(x + 1, y + ypscale - 3, x + xpscale - 1, y + ypscale - 3, lmagenta);
- draw(x + 1, y + 3, x + 1, y + ypscale - 3, lmagenta);
- draw(x + 2, y + 3, x + 2, y + ypscale - 3, lmagenta);
- draw(x + xpscale - 2, y + 3, x + xpscale - 2, y + ypscale - 3, lmagenta);
- draw(x + xpscale - 3, y + 3, x + xpscale - 3, y + ypscale - 3, lmagenta)
- end;
-
- procedure fillsquare(i,j: integer; c: color);
- var
- x, y, xpscale, ypscale, k: integer;
- begin
- xpscale := PIXELSPERCHAR * xscale;
- ypscale := PIXELSPERCHAR * yscale;
- x := ((i * xscale) + xbase) * PIXELSPERCHAR;
- y := ((j * yscale) + ybase) * PIXELSPERCHAR;
- for k := 1 to (ypscale - 2) do
- draw(x + 1, y + k, x + xpscale - 1, y + k, c)
- end;
-
- procedure fillcursor(i, j: integer);
- begin
- fillsquare(i, j, lmagenta)
- end;
-
- procedure clearsquare(i, j: integer);
- begin
- fillsquare(i, j, black)
- end;
-
- procedure drawsquare(k: squarenum; c: sqtype);
-
- var
- i, j, ch: integer;
- begin
- i := k mod 10 - 1;
- j := k div 10 - 1;
- Textcolor(white);
- GotoXY(i*xscale + xbase + 1, j*yscale + ybase + 1);
- ch := (c*4) + $80;
- write(char(ch));
- write(char(ch+1));
- GotoXY(i*xscale + xbase + 1, j*yscale + ybase + 2);
- write(char(ch+2));
- write(char(ch+3));
- if (c = SQTOBEDARK) or (c = SQTOBELIGHT) then
- fillbkgrnd(i, j)
- else if c = SQCURSOR then
- fillcursor(i, j);
- TextColor(lcyan)
- end;
-
- { CRTSTUFF.PAS }
- type
- crtcommand = (HOME, CLEAR, UP, DOWN, LEFT, RIGHT, BEEP);
- g_string = string[255];
- charset = set of char;
-
- procedure crt(cc: crtcommand);
-
- var
- i: integer;
-
- begin
- case cc of
- HOME:
- GotoXY(1,1);
- CLEAR:
- initgraph;
- UP:
- if WhereY > 1 then
- GotoXY(WhereX, WhereY - 1);
- DOWN:
- if WhereY < 24 then
- GotoXY(WhereX, WhereY + 1);
- LEFT:
- if WhereX > 1 then
- GotoXY(WhereX - 1, WhereY);
- RIGHT:
- if WhereX < 40 then
- GotoXY(WhereX + 1, WhereY);
- BEEP:
- for i:=1 to 2 do begin
- Sound(220);
- Delay(100);
- NoSound;
- Delay(50)
- end
- end
- end;
-
- procedure eraseline(row: integer);
- begin
- GotoXY(1, row);
- write(' ':40);
- GotoXY(1, row)
- end;
-
- procedure center(s: g_string; row: integer);
- begin
- eraseline(row);
- GotoXY( (40 - length(s) + 1) div 2, row);
- write(s)
- end;
-
- procedure disptitle(s: g_string);
-
- var
- i, nch: integer;
-
- begin
- center(s, 1);
- end;
-
- function getkey(var ch: char; valid: charset; shiftlock: boolean): char;
- var
- ok: boolean;
- begin
- repeat
- readln(ch);
- if shiftlock then
- ch := UpCase(ch);
- ok := ch in valid;
- if not ok then
- crt(BEEP)
- until ok;
- getkey := ch
- end;
-
- { INITREV.PAS }
- procedure initrev;
- var
- i, j, sv: integer;
-
- begin { initrev }
- sqchar[DARK] := 'B';
- sqchar[LIGHT] := 'W';
- sqchar[EMPTY] := ' ';
- sqchar[BORDER] := '*';
- sqord[11] := 1; sqord[12] := 7; sqord[13] := 2; sqord[14] := 2;
- sqord[22] := 8; sqord[23] := 6; sqord[24] := 5;
- sqord[33] := 3; sqord[34] := 4;
- sqord[44] := 0;
- for j := 1 to 4 do
- for i := j to 4 do
- begin
- sv := sqord[10 * j + i];
- sqord[10 * i + j] := sv;
- sqord[10 * (9 - i) + j] := sv;
- sqord[10 * (9 - j) + i] := sv;
- sqord[10 * j + 9 - i] := sv;
- sqord[10 * i + 9 - j] := sv;
- sqord[10 * (9 - i) + 9 - j] := sv;
- sqord[10 * (9 - j) + 9 - i] := sv
- end;
- delta[NORTH] := -10;
- delta[NORTHEAST] := -9;
- delta[EAST] := 1;
- delta[SOUTHEAST] := 11;
- delta[SOUTH] := 10;
- delta[SOUTHWEST] := 9;
- delta[WEST] := -1;
- delta[NORTHWEST] := -11;
-
- corner[1] := 11; poison2[1, 1] := 12; good2[1, 1] := 13;
- poison2[1, 2] := 21; poison1[1] := 22;
- good2[1, 2] := 31; good1[1] := 33;
-
- corner[2] := 18; poison2[2, 1] := 17; good2[2, 1] := 16;
- poison2[2, 2] := 28; poison1[2] := 27;
- good2[2, 2] := 38; good1[2] := 36;
-
- corner[3] := 81; poison2[3, 1] := 82; good2[3, 1] := 83;
- poison2[3, 2] := 71; poison1[3] := 72;
- good2[3, 2] := 61; good1[3] := 63;
-
- corner[4] := 88; poison2[4, 1] := 87; good2[4, 1] := 86;
- poison2[4, 2] := 78; poison1[4] := 77;
- good2[4, 2] := 68; good1[4] := 66;
-
- for i := 1 to 4 do
- begin
- edge[1, i] := 12 + i;
- edge[2, i] := 28 + 10 * i;
- edge[3, i] := 21 + 10 * i;
- edge[4, i] := 82 + i
- end
- end; { initrev }
-
- { DISPSQUA.PAS }
- procedure dispsquare(k: squarenum; c: contents);
- begin { dispsquare }
- case c of
- LIGHT: drawsquare(k, sqlight);
- DARK : drawsquare(k, sqdark);
- EMPTY: drawsquare(k, sqempty);
- BORDER: drawsquare(k, sqcursor)
- end
- end; { dispsquare }
-
- { ITOS.PAS }
- procedure itos(n, wid: integer; var s: g_string);
- var
- negnum: boolean;
-
- begin { debugproc('itos'); }
- negnum := (n < 0);
- n := abs(n);
- s := '';
- repeat
- s := chr(n mod 10 + 48) + s;
- n := n div 10
- until n = 0;
- if negnum then
- s := '-' + s;
- while length(s) < wid do
- s := ' ' + s;
- end; { itos }
-
- { FLANKING.PAS }
- function flanking(k: squarenum; dir: direction; var bd: board; pl: plcolor):
- boolean;
- var
- ok: boolean;
- opponent: plcolor;
- del: integer;
- begin { flanking }
- ok := FALSE;
- opponent := 1-pl;
- del := delta[dir];
- k := k + del;
- with bd do
- if sq[k] = opponent then
- begin
- repeat
- k := k + del
- until sq[k] <> opponent;
- ok := (sq[k] = pl)
- end;
- flanking := ok
- end; { flanking }
-
- { LEGALMOV.PAS }
- function legalmove(k: squarenum; var bd: board; pl: plcolor): boolean;
- var
- ok: boolean;
- dir: direction;
- begin { legalmove }
- dir := NORTH;
- ok := flanking(k, dir, bd, pl);
- while (dir <> NORTHWEST) and not ok do
- begin
- dir := succ(dir);
- ok := flanking(k, dir, bd, pl)
- end;
- legalmove := ok
- end; { legalmove}
-
- { MAKELIST.PAS }
- function makelist(var legal: movelist; pl: plcolor; var bd: board): integer;
- var
- i: integer;
-
- begin { makelist }
- legal.nmoves := 0;
- with bd.possible do
- for i := 1 to nmoves do
- if legalmove(move[i], bd, pl) then
- begin
- legal.nmoves := legal.nmoves + 1;
- legal.move[legal.nmoves] := move[i]
- end;
- makelist := legal.nmoves
- end; { makelist }
-
- { DELMOVE.PAS }
- procedure delmove(k: squarenum; var list: movelist);
- var
- i: integer;
- begin { debugproc('delmove'); }
- with list do
- begin
- move[nmoves + 1] := k;
- i := 1;
- while move[i] <> k do i := i + 1;
- if i < nmoves + 1 then
- begin
- while i <= nmoves - 1 do
- begin
- move [i] := move[i + 1];
- i := i + 1
- end;
- nmoves := nmoves - 1
- end
- end
- end; { delmove }
-
- { ADDMOVE.PAS }
- procedure addmove(k: squarenum; var list: movelist);
- var
- i: integer;
- begin { debugproc('addmove'); }
- with list do
- begin
- move[nmoves + 1] := k;
- i := 1;
- while move[i] <> k do
- i := i + 1;
- if i = nmoves + 1 then
- nmoves := nmoves + 1
- end
- end; { addmove }
-
- procedure playgame;
- var
- mainboard: board;
- list: movelist;
- gameover, moved: boolean;
- currentplayer: plcolor;
- playertype: array [plcolor] of pltype;
- lookahead: integer;
- k: squarenum;
-
- { SETSQUAR.PAS }
- procedure setsquare(k: squarenum; c: contents);
- begin { debugproc('setsquare'); }
- mainboard.sq[k] := c;
- dispsquare(k, c)
- end; { setsquare }
-
- { DISPSCOR.PAS }
- procedure dispscore;
- var
- s: string[255];
-
- begin { dispscore }
- with mainboard do
- begin
- itos(ndiscs[LIGHT], 2, s);
- GotoXY(37,6);
- write(s);
- itos(ndiscs[DARK], 2, s);
- GotoXY(37,7);
- write(s)
- end
- end; { dispscore }
-
- { INITGAME.PAS }
- procedure initgame;
- var
- i, j: integer;
- ch: char;
-
- begin { initgame }
- with mainboard do
- begin
- for i := 0 to 9 do
- begin
- sq[i] := BORDER;
- sq[i + 90] := BORDER;
- sq[10 * i] := BORDER;
- sq[10 * i + 9] := BORDER
- end;
- ndiscs[LIGHT] := 2;
- ndiscs[DARK] := 2;
- with possible do
- begin
- nmoves := 12;
- move[ 1] := 33;
- move[ 2] := 34;
- move[ 3] := 35;
- move[ 4] := 36;
- move[ 5] := 43;
- move[ 6] := 46;
- move[ 7] := 53;
- move[ 8] := 56;
- move[ 9] := 63;
- move[10] := 64;
- move[11] := 65;
- move[12] := 66
- end
- end;
- for i := 1 to 8 do
- for j := 1 to 8 do
- setsquare(10 * i + j, EMPTY);
- setsquare(44, LIGHT);
- setsquare(55, LIGHT);
- setsquare(45, DARK);
- setsquare(54, DARK);
- for i := 5 to 9 do begin
- GotoXY(21, i);
- write(' ':20)
- end;
- eraseline(23);
- eraseline(24);
- GotoXY(1,23);
- write('Player types: C = computer, H = Human');
- GotoXY(1,24);
- write('White player (C/H): ');
- case getkey(ch, ['C', 'H'], TRUE) of
- 'C': playertype[LIGHT] := COMPUTERPLAYER;
- 'H': playertype[LIGHT] := HUMANPLAYER;
- end;
- eraseline(24);
- GotoXY(1,24);
- write('Black player (C/H): ');
- case getkey(ch, ['C', 'H'], TRUE) of
- 'C': playertype[DARK] := COMPUTERPLAYER;
- 'H': playertype[DARK] := HUMANPLAYER;
- end;
- eraseline(23);
- eraseline(24);
- if (playertype[LIGHT] = COMPUTERPLAYER) or (playertype[DARK] = COMPUTERPLAYER) then
- begin
- GotoXY(1,24);
- write('Enter lookahead for computer (1-6): ');
- lookahead := ord(getkey(ch, ['1'..'6'], FALSE)) - 48;
- GotoXY(28,8);
- write('Lookahead:');
- write(ch)
- end;
- eraseline(24);
- GotoXY(31,6);
- write('White:');
- GotoXY(31,7);
- write('Black:');
- end; { initgame }
-
- function getcomputer(var list: movelist): squarenum;
- var
- max: integer;
- best: squarenum;
-
- { EVAL.PAS }
- function eval(var bd: board; pl: plcolor; ourpl: plcolor): integer;
- const
- K1 = 1; { weighting factor for disc advantage }
- K2 = 3; { weighting factor for mobility }
- K3 = 200; { score for owning corner }
- K4 = -100; { penalty for owning poison1 square }
- K5 = 50; { score for owning good1 square }
- K6 = -15; { penalty for owning poison2 square }
- K7 = 15; { score for owning good2 square }
- K8 = 20; { score for having ownly discs on edge }
- K9 = 30; { score for occupying edge }
-
- var
- list: movelist;
- i, j, score: integer;
- c: contents;
- sideset: set of contents;
- opp: plcolor;
- plmoves: integer;
-
- function endgame: boolean;
- begin
- endgame := FALSE;
- if plmoves = 0 then
- begin
- if makelist(list, 1-pl, bd) = 0 then
- endgame := TRUE
- end
- end;
-
- begin { eval }
- opp := 1 - ourpl;
- with bd do begin
- score := K1 * (ndiscs[ourpl] - ndiscs[opp]);
- plmoves := makelist(list, pl, bd);
- if endgame then
- begin
- if score > 0 then
- eval := maxint
- else if score < 0 then
- eval := -maxint
- else
- eval := 0
- end
- else begin
- if pl = ourpl then
- score := score + k2 * plmoves
- else
- score := score - K2 * plmoves;
- for i := 1 to 4 do begin
- c := sq[corner[i]];
- if c = ourpl then
- score := score + K3
- else if c = opp then
- score := score - K3
- else begin { corner empty, check poison squares }
- c := sq[poison1[i]];
- if c = ourpl then
- score := score + K4
- else if c = opp then
- score := score - K4
- else begin
- c := sq[good1[i]];
- if c = ourpl then
- score := score + K5
- else if c = opp then
- score := score - K5
- end;
- for j := 1 to 2 do begin
- c := sq[poison2[i, j]];
- if c = ourpl then
- score := score + K6
- else if c = opp then
- score := score - K6
- else begin
- c := sq[good2[i, j]];
- if c = ourpl then
- score := score + k7
- else if c = opp then
- score := score - K7
- end
- end
- end
- end;
- for i := 1 to 4 do begin
- sideset := [];
- for j := 1 to 4 do
- sideset := sideset + [sq[edge[i, j]]];
- if sideset = [ourpl] then
- score := score + K9
- else if sideset = [ourpl, EMPTY] then
- score := score + K8
- else if sideset = [opp, EMPTY] then
- score := score - K8
- else if sideset = [opp] then
- score := score - K9
- end;
- eval := score
- end
- end
- end; { eval }
-
- { TRYMOVE.PAS }
- procedure trymove(trysq: squarenum; pl: plcolor; var bd: board);
- var
- dir: direction;
- k1: squarenum;
- opp: plcolor;
- del: integer;
- begin { trymove }
- opp := 1 - pl;
- with bd do begin
- sq[trysq] := pl;
- ndiscs[pl] := ndiscs[pl] + 1;
- delmove(trysq, possible);
- for dir := NORTH to NORTHWEST do begin
- del := delta[dir];
- if flanking(trysq, dir, bd, pl) then begin
- k1 := trysq + del;
- repeat
- sq[k1] := pl;
- ndiscs[pl] := ndiscs[pl] + 1;
- ndiscs[opp] := ndiscs[opp] - 1;
- k1 := k1 + del
- until sq[k1] = pl
- end
- else if sq[trysq + del] = EMPTY then
- addmove(trysq + del, possible)
- end
- end
- end; { trymove }
-
- { SORTLIST.PAS }
- procedure sortlist(var list: movelist);
- var
- i, j, jg, gap, k: integer;
- begin { sortlist }
- with list do begin
- gap := nmoves div 2;
- while gap > 0 do begin
- for i := gap + 1 to nmoves do begin
- j := i - gap;
- while j > 0 do begin
- jg := j + gap;
- if sqord[move[j]] <= sqord[move[jg]] then
- j := 0
- else begin
- k := move[j];
- move[j] := move[jg];
- move[jg] := k
- end;
- j := j - gap
- end
- end;
- gap := gap div 2
- end
- end
- end; { sortlist }
-
- { FINDMAX.PAS }
- function findmin(look: integer; var list: movelist; var bd: board;
- cutoff: integer; var bestmove: squarenum; ourpl: plcolor): integer;
- forward;
-
- function findmax(look: integer; var list: movelist; var bd: board;
- cutoff: integer; var bestmove: squarenum; ourpl: plcolor): integer;
- var
- newlist: movelist;
- newbd: board;
- i, maxscore, score, nm: integer;
- junk: squarenum;
- opp: plcolor;
-
- begin { findmax }
- opp := 1 - ourpl;
- sortlist(list);
- with list do
- if nmoves > 0 then begin
- maxscore := -MAXINT;
- i := 1;
- repeat
- newbd := bd;
- trymove(move[i], ourpl, newbd);
- if look <= 1 then
- score := eval(newbd, opp, ourpl)
- else begin
- nm := makelist(newlist, opp, newbd);
- score := findmin(look - 1, newlist, newbd, maxscore, junk, ourpl)
- end;
- if score > maxscore then begin
- maxscore := score;
- bestmove := move[i]
- end;
- i := i + 1
- until (i > nmoves) or (maxscore >= cutoff)
- end
- else begin { no legal move }
- if look <= 1 then
- maxscore := eval(bd, opp, ourpl)
- else begin
- nm := makelist(newlist, opp, bd);
- maxscore := findmin(look - 1, newlist, bd, -MAXINT, junk, ourpl)
- end
- end;
- findmax := maxscore
- end; { findmax }
-
- { FINDMIN.PAS }
- function findmin;
- var
- newlist: movelist;
- newbd: board;
- i, minscore, score, nm: integer;
- junk: squarenum;
- opp: plcolor;
-
- begin { findmin }
- opp := 1 - ourpl;
- sortlist(list);
- with list do
- if nmoves > 0 then begin
- minscore := MAXINT;
- i := 1;
- repeat
- newbd := bd;
- trymove(move[i], opp, newbd);
- if look <= 1 then
- score := eval(newbd, ourpl, ourpl)
- else begin
- nm := makelist(newlist, ourpl, newbd);
- score := findmax(look - 1, newlist, newbd, minscore, junk, ourpl)
- end;
- if score < minscore then begin
- minscore := score;
- bestmove := move[i]
- end;
- i := i + 1
- until (i > nmoves) or (minscore <= cutoff)
- end
- else begin { no legal move }
- if look <= 1 then
- minscore := eval(bd, ourpl, ourpl)
- else begin
- nm := makelist(newlist, ourpl, bd);
- minscore := findmax(look - 1, newlist, bd, MAXINT, junk, ourpl)
- end
- end;
- findmin := minscore
- end; { findmin }
-
- begin { getcomputer }
- if list.nmoves = 1 then { only 1 legal move }
- getcomputer := list.move[1]
- else begin
- max := findmax(lookahead, list, mainboard, MAXINT, best, currentplayer);
- getcomputer := best
- end
- end; { getcomputer }
-
- { GETHUMAN.PAS }
- procedure makeflip(var fl: movelist; cp: plcolor; mv: squarenum; var bd: board);
- var
- dir: direction;
- k1: squarenum;
- del: integer;
- i: integer;
- begin
- fl.nmoves := 0;
- bd.sq[mv] := cp;
- for dir := NORTH to NORTHWEST do
- begin
- del := delta[dir];
- if flanking(mv, dir, bd, cp) then
- begin
- k1 := mv + del;
- repeat
- fl.nmoves := fl.nmoves + 1;
- fl.move[fl.nmoves] := k1;
- k1 := k1 + del
- until bd.sq[k1] = cp
- end
- end;
- bd.sq[mv] := EMPTY
- end;
-
- function gethuman(var list: movelist): squarenum;
- type
- movekey = (ACCEPT, NEXTMOVE, PREVMOVE);
- var
- i, j: integer;
- ch: char;
- m: movekey;
- fliplist: movelist;
- sq: integer;
-
- function getmovekey: movekey;
- var
- ch: char;
- gotkey: boolean;
-
- begin { getmovekey }
- gotkey := FALSE;
- while not gotkey do
- begin
- read(kbd, ch);
- if ch = char(27) then
- begin
- read(kbd, ch);
- if ch = 'K' then { left arrow }
- begin
- gotkey := TRUE;
- getmovekey := PREVMOVE
- end
- else if ch = 'M' then { right arrow }
- begin
- gotkey := TRUE;
- getmovekey := NEXTMOVE
- end
- else
- crt(BEEP);
- end
- else { ch <> char(27) }
- if ch = char(13) then
- begin
- gotkey := TRUE;
- getmovekey := ACCEPT
- end
- else
- crt(BEEP);
- end { while not gotkey }
- end; { getmovekey }
-
- begin { gethuman }
- i := 1;
- crt(BEEP);
- with list do
- begin
- repeat
- dispsquare(move[i], BORDER);
- if currentplayer = dark then
- sq := SQTOBEDARK
- else
- sq := SQTOBELIGHT;
- makeflip(fliplist, currentplayer, move[i], mainboard);
- for j := 1 to fliplist.nmoves do
- drawsquare(fliplist.move[j], sq);
- m := getmovekey;
- for j := fliplist.nmoves downto 1 do
- drawsquare(fliplist.move[j], 1-currentplayer);
- dispsquare(move[i], EMPTY);
- case m of
- PREVMOVE:
- begin
- i := i - 1;
- if i < 1 then i := nmoves
- end;
- NEXTMOVE:
- begin
- i := i + 1;
- if i > nmoves then i := 1
- end
- end
- until m = ACCEPT;
- gethuman := move[i]
- end
- end; { gethuman }
-
- { GETMOVE.PAS }
- function getmove(var list: movelist; pl: pltype): squarenum;
-
- begin { getmove }
- Textcolor(lmagenta);
- if currentplayer = LIGHT then
- begin
- GotoXY(31,6);
- write('White:')
- end
- else
- begin
- GotoXY(31,7);
- write('Black:')
- end;
- Textcolor(lcyan);
- if pl = COMPUTERPLAYER then
- getmove := getcomputer(list)
- else
- getmove := gethuman(list);
- if currentplayer = LIGHT then
- begin
- GotoXY(31,6);
- write('White:')
- end
- else
- begin
- GotoXY(31,7);
- write('Black:')
- end
- end; { getmove }
-
- { MAKEMOVE.PAS }
- procedure makemove(k: squarenum; pl: plcolor);
- var
- dir: direction;
- k1: squarenum;
- opponent: plcolor;
- del: integer;
-
- begin { makemove }
- setsquare(k, pl);
- opponent := 1 - pl;
- with mainboard do
- begin
- ndiscs[pl] := ndiscs[pl] + 1;
- delmove(k, possible);
- for dir := NORTH to NORTHWEST do
- begin
- del := delta[dir];
- if flanking(k, dir, mainboard, pl) then
- begin
- k1 := k + del;
- repeat
- setsquare(k1, pl);
- ndiscs[pl] := ndiscs[pl] + 1;
- ndiscs[opponent] := ndiscs[opponent] - 1;
- k1 := k1 + del
- until sq[k1] = pl
- end
- else if sq[k + del] = EMPTY then
- addmove(k + del, possible)
- end
- end
- end; { makemove }
-
- { DECLWINN.PAS }
-
- procedure declarewinner;
- var
- diff: integer;
- s: string[255];
- begin { declarewinner }
- with mainboard do
- diff := ndiscs[LIGHT] - ndiscs[DARK];
- if diff > 0 then
- begin
- itos(diff, 0, s);
- GotoXY(25,9);
- write('White won by ' + s)
- end
- else if diff < 0 then
- begin
- itos(-diff, 0, s);
- GotoXY(25,9);
- write('Black won by ' + s)
- end
- else begin
- GotoXY(27,9);
- write('Game is tied!')
- end
- end; { declarewinner }
-
- begin { playgame }
- initgame;
- currentplayer := DARK;
- gameover := FALSE;
- moved := TRUE;
- repeat
- dispscore;
- if makelist(list, currentplayer, mainboard) > 0 then
- begin
- moved := TRUE;
- k := getmove(list, playertype[currentplayer]);
- makemove(k, currentplayer)
- end
- else if moved then
- moved := FALSE
- else
- gameover := TRUE;
- currentplayer := 1-currentplayer
- until gameover;
- declarewinner
- end; { playgame }
-
- begin { main program }
- initgraph;
- disptitle('R E V E R S I');
- center('Version 1.4.1', 2);
- initrev;
- dispgrid;
- buildsquare;
- repeat
- playgame;
- eraseline(24);
- GotoXY(1,24);
- write('Play again? (Y/N): ');
- ch := getkey(ch, ['Y', 'N'], TRUE);
- eraseline(24)
- until ch = 'N';
- Textmode;
- ClrScr
- end { reversi } .