1 (* Copyright (C) 2010 Free Software Foundation, Inc. *)
2 (* This file is part of GNU Modula-2.
4 GNU Modula-2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
9 GNU Modula-2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License along
15 with gm2; see the file COPYING. If not, write to the Free Software
16 Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *)
20 FROM STextIO IMPORT WriteString, WriteLn, WriteChar, ReadToken, SkipLine ;
21 FROM SWholeIO IMPORT WriteCard, WriteInt ;
22 FROM Strings IMPORT Length ;
23 FROM Selective IMPORT Timeval, GetTimeOfDay, GetTime, InitTime, KillTime ;
24 FROM WholeStr IMPORT StrToCard, ConvResults ;
25 FROM SYSTEM IMPORT CARDINAL8 ;
32 BoardSize = BoardX * BoardY ;
33 Pieces = 19 ; (* total pieces per player on the board *)
34 PieceHeap = 4000 ; (* maximum moves we will examine per ply *)
38 LooseScore = -WinScore ;
40 Thinking = 10 ; (* how many seconds can the program think? *)
41 slowEvaluation = FALSE ;
45 Squares = [0..BoardSize-1] ;
46 SoS = SET OF Squares ;
47 Colour = (Blue, Red, Green, White) ;
50 used : SoS ; (* is the square used at all? *)
51 colour: ARRAY [0..1] OF SoS ; (* if so which colour occupies the square? *)
52 pieces: ARRAY [MIN(Colour)..MAX(Colour)] OF ARRAY [1..Pieces] OF CARDINAL8 ;
53 home : ARRAY [MIN(Colour)..MAX(Colour)] OF CARDINAL ;
57 pieceHead: ARRAY [0..Pieces] OF CARDINAL ; (* pieceHead[0] is start of peg 1 moves in the heap *)
58 pieceList: ARRAY [0..PieceHeap] OF CARDINAL8 ; (* pieceHead[1] is start of peg 2 moves in the heap *)
65 list: ARRAY Squares OF CARDINAL ;
69 graph: ARRAY Squares OF Reachable ;
74 homeBase: ARRAY [MIN(Colour)..MAX(Colour)] OF SoS ;
78 +-----------------------------------------------------------------+
79 | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
81 | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
83 | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
85 | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
87 | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
89 | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
91 | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
93 | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
95 | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
97 | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
99 | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
101 | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
103 | 48 49 \50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
105 | 32 33 34 \35 36 37 38 39 40 41 42 43 44 45 46 47 |
107 | 16 17 18 19| 20 21 22 23 24 25 26 27 28 29 30 31 |
109 | 0 1 2 3| 4 5 6 7 8 9 10 11 12 13 14 15 |
110 +-----------------------------------------------------------------+
127 PROCEDURE Min (a, b: INTEGER) : INTEGER ;
142 PROCEDURE assert (b: BOOLEAN) ;
146 WriteString('assert failed') ; WriteLn ;
153 initGraph - initialise, g, to empty.
156 PROCEDURE initGraph (VAR g: Graph) ;
160 FOR i := MIN(Squares) TO MAX(Squares) DO
162 g.graph[i].prev := MAX(Squares)+1 ;
163 g.graph[i].dist := MAX(Squares)+1
169 isUsed - return whether a square, p, is in use on board, b.
172 PROCEDURE isUsed (VAR b: Board; p: CARDINAL) : BOOLEAN ;
179 isColour - return TRUE if a square, p, is used and contains a
183 PROCEDURE isColour (VAR b: Board; p: CARDINAL; c: Colour) : BOOLEAN ;
190 Blue: RETURN (NOT (p IN colour[0])) AND (NOT (p IN colour[1])) |
191 Red : RETURN (p IN colour[0]) AND (NOT (p IN colour[1])) |
192 Green: RETURN (NOT (p IN colour[0])) AND (p IN colour[1]) |
193 White: RETURN (p IN colour[0]) AND (p IN colour[1])
206 PROCEDURE dumpBase (c: Colour) ;
210 WriteString('dumpBase(c) where ORD(c)=') ; WriteCard(ORD(c), 3) ; WriteLn ;
212 FOR i := 0 TO MAX(Squares) DO
213 IF (n>0) AND ((n MOD 16) = 0)
231 addPiece - adds a piece, pos, of colour, c, to the board, b.
234 PROCEDURE addPiece (VAR b: Board; pos: CARDINAL; c: Colour; piece: CARDINAL) ;
240 IF pos IN homeBase[c]
242 WriteString('found ') ; WriteCard(pos, 3) ; WriteString(' in homeBase[c]') ;
252 Blue: EXCL(colour[0], pos) ;
253 EXCL(colour[1], pos) |
254 Red : INCL(colour[0], pos) ;
255 EXCL(colour[1], pos) |
256 Green: EXCL(colour[0], pos) ;
257 INCL(colour[1], pos) |
258 White: INCL(colour[0], pos) ;
262 pieces[c][piece] := pos ;
263 IF pos IN homeBase[c]
272 subPiece - removes a piece at, pos, from the board, b.
275 PROCEDURE subPiece (VAR b: Board; pos: CARDINAL; c: Colour) ;
279 IF pos IN homeBase[c]
291 PROCEDURE ifFreeAdd (condition: BOOLEAN; VAR b: Board; t: INTEGER; p: CARDINAL; c: Colour; VAR m: Moves) ;
293 IF condition AND (NOT isUsed(b, t)) AND (NOT isRecorded(m, t, p))
301 recordMove - adds tile, t, to piece, p, list of moves.
304 PROCEDURE recordMove (VAR m: Moves; t: INTEGER; p: CARDINAL) ;
307 pieceList[pieceHead[p]] := t ;
314 isRecorded - returns TRUE if tile, t, has been already entered as a
315 possible move for piece, p, on move list, m.
318 PROCEDURE isRecorded (VAR m: Moves; t: INTEGER; p: CARDINAL) : BOOLEAN ;
324 i := pieceHead[p-1]+1 ;
326 IF pieceList[i] = VAL (CARDINAL8, t)
338 addSingle - adds a single move from a piece, testing all eight one square
342 PROCEDURE addSingle (VAR b: Board; VAR m: Moves; c: Colour; p: CARDINAL) ;
348 t := VAL(INTEGER, pieces[c][p]) ;
351 (* vertical and horizontal *)
353 ifFreeAdd(x>0, b, t-1, p, c, m) ; (* -1, 0 *)
354 ifFreeAdd(x<BoardX-1, b, t+1, p, c, m) ; (* 1, 0 *)
355 ifFreeAdd(y>0, b, t-BoardX, p, c, m) ; (* 0, -1 *)
356 ifFreeAdd(y<BoardY-1, b, t+BoardX, p, c, m) ; (* 0, 1 *)
359 ifFreeAdd((x>0) AND (y>0), b, t-(BoardX+1), p, c, m) ; (* -1, -1 *)
360 ifFreeAdd((x<BoardX-1) AND (y<BoardY-1), b, t+BoardX+1, p, c, m) ; (* 1, 1 *)
362 ifFreeAdd((x<BoardX-1) AND (y>0), b, t-(BoardX-1), p, c, m) ; (* 1, -1 *)
363 ifFreeAdd((x>0) AND (y<BoardY-1), b, t+(BoardX-1), p, c, m) (* -1, 1 *)
372 PROCEDURE addMultipleV (VAR b: Board; VAR m: Moves; c: Colour; p: CARDINAL; x, y, i, j: INTEGER) ;
382 e := (BoardY-y) DIV 2
391 e := (BoardX-x) DIV 2
399 e := Min((BoardX-x) DIV 2, (BoardY-y) DIV 2)
400 ELSIF (i=-1) AND (j=1)
402 e := Min(x DIV 2, (BoardY-y) DIV 2)
403 ELSIF (i=-1) AND (j=-1)
405 e := Min(x DIV 2, y DIV 2)
408 e := Min((BoardX-x) DIV 2, y DIV 2)
414 (* no point searching further as there is no room for the reflective jump *)
419 IF (x<0) OR (y<0) OR (x>=BoardX) OR (y>=BoardY)
423 t := VAL(CARDINAL, y)*BoardX+VAL(CARDINAL, x) ;
426 (* found pivot, keep looking for the destination *)
444 IF (x<0) OR (y<0) OR (x>=BoardX) OR (y>=BoardY)
448 t := VAL(CARDINAL, y)*BoardX+VAL(CARDINAL, x) ;
455 IF NOT isRecorded(m, t, p)
459 WriteString('adding move ') ; WriteCard(t, 0) ; WriteLn
471 addMultiple - adds moves which involve jumping. Current peg, p, is at at position
472 indicated by, m.pieceList[low].
475 PROCEDURE addMultiple (VAR b: Board; VAR m: Moves; c: Colour; p: CARDINAL; low: CARDINAL) ;
481 WHILE low<m.pieceHead[p] DO
482 t := VAL(INTEGER, m.pieceList[low]) ;
485 addMultipleV(b, m, c, p, x, y, -1, 0) ;
486 addMultipleV(b, m, c, p, x, y, -1, 1) ;
487 addMultipleV(b, m, c, p, x, y, -1, -1) ;
489 addMultipleV(b, m, c, p, x, y, 1, 0) ;
490 addMultipleV(b, m, c, p, x, y, 1, 1) ;
491 addMultipleV(b, m, c, p, x, y, 1, -1) ;
493 addMultipleV(b, m, c, p, x, y, 0, 1) ;
494 addMultipleV(b, m, c, p, x, y, 0, -1) ;
502 genMove - generate the moves for peg, p.
505 PROCEDURE genMove (VAR b: Board; VAR m: Moves; c: Colour; p: CARDINAL) ;
507 m.pieceHead[p] := m.pieceHead[p-1] ;
508 recordMove(m, b.pieces[c][p], p) ;
509 (* record the current position so we can ignore moving back to it *)
510 addMultiple(b, m, c, p, m.pieceHead[p]-1) ;
511 addSingle(b, m, c, p)
516 genMoves - generate the list of moves for colour, c, on board, b.
517 The board, b, is unaltered despite being passed by reference.
520 PROCEDURE genMoves (VAR b: Board; VAR m: Moves; c: Colour) ;
525 m.pieceHead[0] := 0 ;
526 FOR peg := 1 TO Pieces DO
527 pos := b.pieces[c][peg] ;
528 subPiece(b, pos, c) ; (* remove this peg while jumping (so we dont jump over ourself) *)
529 genMove(b, m, c, peg) ;
530 addPiece(b, pos, c, peg) (* restore the peg *)
539 PROCEDURE addToGraph (VAR g: Graph; from, to: CARDINAL) ;
543 WITH g.graph[from] DO
563 PROCEDURE ifFreeRecord (condition: BOOLEAN; VAR b: Board; t: INTEGER; p: CARDINAL;
564 c: Colour; VAR m: Moves; from: CARDINAL; VAR g: Graph) ;
566 IF condition AND (NOT isUsed(b, t)) AND (NOT isRecorded(m, t, p))
568 recordMove(m, t, p) ;
569 addToGraph(g, from, t)
575 recordSingle - adds a single move from a piece, testing all eight one square
579 PROCEDURE recordSingle (VAR b: Board; VAR m: Moves; c: Colour;
580 p: CARDINAL; from: CARDINAL; VAR g: Graph) ;
586 t := VAL(INTEGER, pieces[c][p]) ;
589 (* vertical and horizontal *)
591 ifFreeRecord(x>0, b, t-1, p, c, m, from, g) ; (* -1, 0 *)
592 ifFreeRecord(x<BoardX-1, b, t+1, p, c, m, from, g) ; (* 1, 0 *)
593 ifFreeRecord(y>0, b, t-BoardX, p, c, m, from, g) ; (* 0, -1 *)
594 ifFreeRecord(y<BoardY-1, b, t+BoardX, p, c, m, from, g) ; (* 0, 1 *)
597 ifFreeRecord((x>0) AND (y>0), b, t-(BoardX+1), p, c, m, from, g) ; (* -1, -1 *)
598 ifFreeRecord((x<BoardX-1) AND (y<BoardY-1), b, t+BoardX+1, p, c, m, from, g) ; (* 1, 1 *)
600 ifFreeRecord((x<BoardX-1) AND (y>0), b, t-(BoardX-1), p, c, m, from, g) ; (* 1, -1 *)
601 ifFreeRecord((x>0) AND (y<BoardY-1), b, t+(BoardX-1), p, c, m, from, g) (* -1, 1 *)
610 PROCEDURE recordMultipleV (VAR b: Board; VAR m: Moves; c: Colour;
611 p: CARDINAL; x, y, i, j: INTEGER;
612 from: CARDINAL; VAR g: Graph) ;
622 e := (BoardY-y) DIV 2
631 e := (BoardX-x) DIV 2
639 e := Min((BoardX-x) DIV 2, (BoardY-y) DIV 2)
640 ELSIF (i=-1) AND (j=1)
642 e := Min(x DIV 2, (BoardY-y) DIV 2)
643 ELSIF (i=-1) AND (j=-1)
645 e := Min(x DIV 2, y DIV 2)
648 e := Min((BoardX-x) DIV 2, y DIV 2)
654 (* no point searching further as there is no room for the reflective jump *)
659 IF (x<0) OR (y<0) OR (x>=BoardX) OR (y>=BoardY)
663 t := VAL(CARDINAL, y)*BoardX+VAL(CARDINAL, x) ;
666 (* found pivot, keep looking for the destination *)
670 IF (x<0) OR (y<0) OR (x>=BoardX) OR (y>=BoardY)
674 t := VAL(CARDINAL, y)*BoardX+VAL(CARDINAL, x) ;
681 IF NOT isRecorded(m, t, p)
685 WriteString('adding move ') ; WriteCard(t, 0) ; WriteLn
687 recordMove(m, t, p) ;
688 addToGraph(g, from, t) ;
689 addToGraph(g, t, from)
695 END recordMultipleV ;
699 recordMultiple - adds moves which involve jumping. Current peg, p, is at at position
700 indicated by, m.pieceList[low].
703 PROCEDURE recordMultiple (VAR b: Board; VAR m: Moves; c: Colour;
704 p: CARDINAL; low: CARDINAL; VAR g: Graph) ;
710 WHILE low<m.pieceHead[p] DO
711 from := VAL(INTEGER, m.pieceList[low]) ;
712 x := from MOD BoardX ;
713 y := from DIV BoardX ;
714 recordMultipleV(b, m, c, p, x, y, -1, 0, from, g) ;
715 recordMultipleV(b, m, c, p, x, y, -1, 1, from, g) ;
716 recordMultipleV(b, m, c, p, x, y, -1, -1, from, g) ;
718 recordMultipleV(b, m, c, p, x, y, 1, 0, from, g) ;
719 recordMultipleV(b, m, c, p, x, y, 1, 1, from, g) ;
720 recordMultipleV(b, m, c, p, x, y, 1, -1, from, g) ;
722 recordMultipleV(b, m, c, p, x, y, 0, 1, from, g) ;
723 recordMultipleV(b, m, c, p, x, y, 0, -1, from, g) ;
731 recMove - generate the moves for peg, p.
734 PROCEDURE recMove (VAR b: Board; VAR m: Moves; c: Colour;
735 p: CARDINAL; from: CARDINAL; VAR g: Graph) ;
737 m.pieceHead[p-1] := 0 ;
738 m.pieceHead[p] := 0 ;
739 recordMove(m, from, p) ;
740 (* record the current position so we can ignore moving back to it *)
741 recordMultiple(b, m, c, p, m.pieceHead[p]-1, g) ;
742 recordSingle(b, m, c, p, from, g)
747 recMoves - generate the list of moves for colour, c, on board, b, and record each
749 The board, b, is unaltered despite being passed by reference.
752 PROCEDURE recMoves (VAR b: Board; VAR m: Moves; c: Colour;
753 peg: CARDINAL; from: CARDINAL; VAR g: Graph) ;
757 pos := b.pieces[c][peg] ;
758 subPiece(b, pos, c) ; (* remove this peg while jumping (so we dont jump over ourself) *)
760 recMove(b, m, c, peg, from, g) ;
761 addPiece(b, pos, c, peg) (* restore the peg *)
766 WriteColour - displays the colour, c.
769 PROCEDURE WriteColour (c: Colour) ;
773 White: WriteString('white') |
774 Blue : WriteString('blue') |
775 Green: WriteString('green') |
776 Red : WriteString('red')
786 PROCEDURE getFirstPos (s: ARRAY OF CHAR; VAR b: Board; c: Colour) : CARDINAL ;
798 displayAllMoves(b, c)
799 ELSIF (x>='A') AND (x<='P')
801 from := ORD (x) - ORD ('A') ;
805 StrToCard(s, y, res) ;
806 IF (res=strAllRight) AND ((y=0) OR (y>BoardY))
808 WriteString('Please enter a number between [1-16]') ; WriteLn
810 from := from+(y-1)*BoardY ;
811 IF isUsed(b, from) AND isColour(b, from, c)
815 WriteString('That position is occupied by your opponent') ; WriteLn
820 WriteString('please enter a letter [A-P] followed by a number [1-16]') ; WriteLn
831 PROCEDURE getSecondPos (s: ARRAY OF CHAR; VAR b: Board; c: Colour; peg: CARDINAL) : CARDINAL ;
843 displayMovesPeg(b, c, peg) ;
844 displayBoardPeg(b, c, peg)
845 ELSIF (x>='A') AND (x<='P')
847 from := ORD (x) - ORD ('A') ;
851 StrToCard(s, y, res) ;
852 IF (res=strAllRight) AND ((y=0) OR (y>BoardY))
854 WriteString('Please enter a number between [1-16]') ; WriteLn
856 from := from+(y-1)*BoardY ;
857 IF NOT isUsed(b, from)
860 ELSIF isColour(b, from, c)
862 WriteString('That position is already occupied by another of your pegs') ; WriteLn
864 WriteString('That position is occupied by your opponent') ; WriteLn
869 WriteString('please enter a letter [A-P] followed by a number [1-16]') ; WriteLn
880 PROCEDURE getPeg (VAR b: Board; c: Colour; from: CARDINAL) : CARDINAL ;
884 FOR p := 1 TO Pieces DO
885 IF b.pieces[c][p] = VAL (CARDINAL8, from)
899 PROCEDURE checkLegal (VAR b: Board; col: Colour; from, to: CARDINAL; peg: CARDINAL) : BOOLEAN ;
904 IF (to=BoardSize) OR (from=BoardSize)
908 genMoves(b, m, col) ;
909 IF VAL (CARDINAL8, from) # b.pieces[col][peg]
913 i := m.pieceHead[peg-1]+1 ; (* skip the initial move *)
914 j := m.pieceHead[peg] ;
916 IF VAL (CARDINAL8, to) = m.pieceList[i]
927 noOfMoves - returns the number of moves held in, m.
930 PROCEDURE noOfMoves (VAR m: Moves) : CARDINAL ;
932 n, p, i, j: CARDINAL ;
935 FOR p := 1 TO Pieces DO
936 i := m.pieceHead[p-1]+1 ; (* skip the initial move *)
937 j := m.pieceHead[p] ;
948 askMove - returns a move entered.
951 PROCEDURE askMove (VAR b: Board; c: Colour; VAR peg: CARDINAL) : CARDINAL ;
953 s : ARRAY [0..80] OF CHAR ;
959 WriteString('Please enter your move, from, ') ;
964 from := getFirstPos(s, b, c) ;
967 WriteString('please try again...') ; WriteLn
969 WriteString('now please enter your move, to, ') ;
974 peg := getPeg(b, c, from) ;
975 to := getSecondPos(s, b, c, peg) ;
976 IF checkLegal(b, c, from, to, peg)
978 WriteString('you are ') ;
979 showMove(b, c, peg, from, to) ;
988 opponent - returns the opponents colour.
991 PROCEDURE opponent (col: Colour) : Colour ;
1003 maximumScore - returns TRUE if the maximim score was found.
1006 PROCEDURE maximumScore (score: INTEGER) : BOOLEAN ;
1008 RETURN (score<=MinScore) OR (score>=MaxScore)
1013 calcScoreForPos - returns the score for Colour, c, pos, on Board, b.
1016 PROCEDURE calcScoreForPos (VAR b: Board; c: Colour; pos: CARDINAL) : INTEGER ;
1023 pos := (BoardSize-1) - pos
1028 HALT (* not implemented yet *)
1030 IF pos IN homeBase[c]
1037 (* our score is dependant upon how far this piece is away from the opposite corner *)
1038 x := pos MOD BoardX ;
1039 y := pos DIV BoardY ;
1042 (* max squares from 0,0 *)
1043 RETURN BoardX-x+home
1045 RETURN BoardY-y+home
1047 END calcScoreForPos ;
1051 calcScoreFor - returns the score for Colour, c.
1054 PROCEDURE calcScoreFor (VAR b: Board; c: Colour) : INTEGER ;
1060 FOR p := 1 TO Pieces DO
1061 INC(score, calcScoreForPos(b, c, b.pieces[c][p]))
1071 PROCEDURE updateMove (VAR b: Board; col: Colour; peg: CARDINAL; topos: CARDINAL) ;
1075 frompos := b.pieces[col][peg] ;
1076 subPiece(b, frompos, col) ;
1077 addPiece(b, topos, col, peg)
1085 PROCEDURE retractMove (VAR b: Board; col: Colour; peg: CARDINAL; topos: CARDINAL) ;
1087 updateMove(b, col, peg, topos)
1092 calcScore - make the move and update the score.
1095 PROCEDURE calcScore (VAR b: Board; score: INTEGER; peg: CARDINAL;
1096 topos: CARDINAL; col: Colour) : INTEGER ;
1102 (* compute the score by examine each peg in turn *)
1103 updateMove(b, col, peg, topos) ;
1105 (* check whether one side has won *)
1106 IF b.home[Blue]=Pieces
1109 ELSIF b.home[Red]=Pieces
1114 RETURN calcScoreFor(b, Blue) - calcScoreFor(b, Red)
1116 i := calcScoreForPos(b, col, b.pieces[col][peg]) ;
1117 updateMove(b, col, peg, topos) ; (* move the peg *)
1119 (* check whether one side has won *)
1120 IF b.home[Blue]=Pieces
1123 ELSIF b.home[Red]=Pieces
1128 j := calcScoreForPos(b, col, topos) ;
1131 score := score + i - j
1133 score := score - i + j
1137 k := calcScoreFor(b, Blue) - calcScoreFor(b, Red) ;
1149 alphaBeta - returns the score estimated should move, pos, be chosen.
1150 The board, b, and score is in the state _before_ move pos
1154 PROCEDURE alphaBeta (peg: CARDINAL; frompos, topos: CARDINAL;
1155 VAR b: Board; col: Colour;
1157 alpha, beta, score: INTEGER) : INTEGER ;
1163 from, to: CARDINAL ;
1166 score := calcScore(b, score, peg, topos, col) ; (* make move and update score *)
1167 IF (depth=0) OR maximumScore(score)
1169 retractMove(b, col, peg, frompos) ;
1173 RETURN score+VAL(INTEGER, depth)
1175 RETURN score-VAL(INTEGER, depth)
1178 op := opponent(col) ;
1179 genMoves(b, m, op) ;
1182 (* blue to move, move is possible, continue searching *)
1183 FOR p := 1 TO Pieces DO
1184 from := b.pieces[op][p] ;
1185 i := m.pieceHead[p-1]+1 ; (* skip the initial move *)
1186 j := m.pieceHead[p] ;
1188 to := m.pieceList[i] ;
1189 try := alphaBeta(p, from, to,
1190 b, op, depth-1, alpha, beta, score) ;
1193 (* found a better move *)
1198 retractMove(b, col, peg, frompos) ;
1204 retractMove(b, col, peg, frompos) ;
1207 (* red to move, move is possible, continue searching *)
1208 FOR p := 1 TO Pieces DO
1209 from := b.pieces[op][p] ;
1210 i := m.pieceHead[p-1]+1 ; (* skip the initial move *)
1211 j := m.pieceHead[p] ;
1213 to := m.pieceList[i] ;
1214 try := alphaBeta(p, from, to,
1215 b, op, depth-1, alpha, beta, score) ;
1218 (* found a better move *)
1223 (* no point searching further as Red would choose
1224 a different previous move *)
1225 retractMove(b, col, peg, frompos) ;
1231 retractMove(b, col, peg, frompos) ;
1232 RETURN beta (* the best score for a move Blue has found *)
1239 makeMove - computer makes a move for colour, col.
1242 PROCEDURE makeMove (VAR b: Board; col: Colour; score: INTEGER; VAR peg: CARDINAL) : INTEGER ;
1247 topos, to : CARDINAL ;
1248 start, end: Timeval ;
1252 secE, i, j: CARDINAL ;
1255 outOfTime : BOOLEAN ;
1257 start := InitTime(0, 0) ;
1258 end := InitTime(0, 0) ;
1260 r := GetTimeOfDay(start) ;
1261 best := MinScore-1 ; (* worst than minimum score so we will choose a loosing move if forced *)
1265 genMoves(b, m, col) ;
1266 no := noOfMoves(m) ;
1268 outOfTime := FALSE ;
1270 frompos := BoardSize ;
1271 topos := BoardSize ;
1273 WriteString("I'm going to look ") ;
1274 WriteCard(plies, 0) ; WriteString(' moves ahead') ; WriteLn ;
1276 FOR p := 1 TO Pieces DO
1277 from := b.pieces[col][p] ;
1278 i := m.pieceHead[p-1]+1 ; (* skip the initial move *)
1279 j := m.pieceHead[p] ;
1282 (* only one move and this peg can move, therefore dont bother evaluating the move, just play it *)
1283 to := m.pieceList[i] ;
1289 WHILE (i<j) AND (NOT outOfTime) DO
1290 r := GetTimeOfDay(end) ;
1291 GetTime(start, secS, usec) ;
1292 GetTime(end, secE, usec) ;
1293 outOfTime := (secE-secS > Thinking) ;
1297 WriteString('out of time...') ; WriteLn
1299 to := m.pieceList[i] ;
1300 try := alphaBeta(p, from, to,
1302 MinScore, MaxScore, score) ;
1315 IF (NOT outOfTime) AND (frompos<BoardSize) AND (topos<BoardSize)
1317 WriteString('so far I think the best move is from') ;
1318 writePosition(frompos) ;
1319 WriteString(' to') ;
1320 writePosition(topos) ;
1324 UNTIL (no<2) OR outOfTime ;
1328 WriteString('I think I can force a win') ; WriteLn
1330 IF best <= LooseScore
1332 WriteString('You should be able to force a win') ; WriteLn
1337 WriteString('I can only play one move, so there is little point wasting time') ; WriteLn
1340 WriteString('I cannot move, so there is little point wasting time') ; WriteLn
1342 WriteString('I took ') ; WriteCard(secE-secS, 0) ;
1343 WriteString(' seconds and evaluated ') ;
1344 WriteCard(count, 0) ; WriteString(' positions,') ; WriteLn ;
1347 start := KillTime(start) ;
1348 end := KillTime(end) ;
1369 peg := getPeg(b, c, 4) ;
1370 displayBoardPeg(b, c, peg) ;
1372 s := calcScore(b, s, peg, to, c) ;
1378 peg := getPeg(b, c, 12*BoardX+15) ;
1379 to := 12*BoardX+13 ;
1380 s := calcScore(b, s, peg, to, c) ;
1381 displayBoardPeg(b, c, peg) ;
1387 peg := getPeg(b, c, 36) ;
1389 displayBoardPeg(b, c, peg) ;
1391 s := calcScore(b, s, peg, to, c) ;
1392 displayBoardPeg(b, c, peg) ;
1401 PROCEDURE displayHow (from, to: CARDINAL; VAR rec: ARRAY OF CARDINAL; r: CARDINAL) ;
1405 writePosition(from) ; WriteString(' can move to ') ; writePosition(to) ; WriteString(' by: ') ;
1407 WHILE (i<r) AND (i<=HIGH(rec)) DO
1408 writePosition(rec[i])
1418 PROCEDURE addToList (VAR choices: ARRAY OF CARDINAL; VAR n: CARDINAL; from: CARDINAL) ;
1430 choices[n] := from ;
1439 PROCEDURE subBest (VAR choices: ARRAY OF CARDINAL; VAR n: CARDINAL; VAR g: Graph) : CARDINAL ;
1445 b := g.graph[k].dist ;
1448 IF g.graph[choices[i]].dist<b
1451 b := g.graph[k].dist
1455 (* remove, k, from, choices *)
1461 choices[i] := choices[j] ;
1478 PROCEDURE dijkstra (from, to: CARDINAL; VAR g: Graph) ;
1481 choices: ARRAY Squares OF CARDINAL ;
1486 g.graph[from].dist := 0 ;
1487 g.graph[from].prev := from ;
1488 visited := SoS{from} ;
1490 addToList(choices, n, from) ;
1492 u := subBest(choices, n, g) ;
1501 IF NOT (v IN visited)
1504 addToList(choices, n, v) ;
1506 IF alt<g.graph[v].dist
1508 g.graph[v].dist := alt ;
1509 g.graph[v].prev := u
1524 PROCEDURE showRoute (from, to: CARDINAL; VAR g: Graph) ;
1528 showRoute(from, g.graph[to].prev, g)
1532 WriteString(' from')
1541 showMove - show how, peg, can move, from, to, on board, b.
1544 PROCEDURE showMove (VAR b: Board;
1545 c: Colour; peg: CARDINAL; from, to: CARDINAL) ;
1550 recMoves(b, m, c, peg, from, g) ;
1551 dijkstra(from, to, g) ;
1552 WriteString('moving peg') ;
1553 showRoute(from, to, g) ;
1568 to, from: CARDINAL ;
1574 RETURN ; (* remove this line of code if you really want to play the game. *)
1576 to := askMove(b, c, peg) ;
1577 s := calcScore(b, s, peg, to, c) ;
1579 WriteString('Current score = ') ; WriteInt(s, 0) ; WriteLn ;
1582 WriteString('Well done you win') ; WriteLn ;
1586 to := makeMove(b, c, s, peg) ;
1589 WriteString('I cannot move') ; WriteLn
1591 from := b.pieces[c][peg] ;
1592 WriteString('I am ') ;
1593 showMove(b, c, peg, from, to) ;
1594 s := calcScore(b, s, peg, to, c) ;
1596 WriteString('Current score = ') ; WriteInt(s, 0) ; WriteLn ;
1599 WriteString('Good try, but I win') ; WriteLn ;
1612 PROCEDURE writePosition (x: CARDINAL) ;
1615 WriteChar(CHR(ORD('a')+x MOD BoardX)) ;
1616 WriteCard(x DIV BoardX+1, 0)
1621 displayMovesForPeg -
1624 PROCEDURE displayMovesForPeg (VAR b: Board; m: Moves; c: Colour; peg: CARDINAL) ;
1628 WriteString('peg at') ;
1629 writePosition(b.pieces[c][peg]) ;
1630 IF m.pieceHead[peg-1]+1<m.pieceHead[peg]
1632 WriteString(' can move to ') ;
1633 i := m.pieceHead[peg-1]+1 ; (* skip the initial move *)
1634 j := m.pieceHead[peg] ;
1636 writePosition(m.pieceList[i]) ;
1642 WriteString(' cannot move') ; WriteLn
1644 END displayMovesForPeg ;
1651 PROCEDURE displayMoves (VAR b: Board; m: Moves; c: Colour) ;
1655 WriteString('possible moves are ') ; WriteLn ;
1656 FOR p := 1 TO Pieces DO
1657 IF m.pieceHead[p-1]+1<m.pieceHead[p]
1659 WriteString('piece at position ') ;
1660 writePosition(b.pieces[c][p]) ;
1661 WriteString(' can move to ') ;
1662 i := m.pieceHead[p-1]+1 ; (* skip the initial move *)
1663 j := m.pieceHead[p] ;
1665 writePosition(m.pieceList[i]) ;
1679 PROCEDURE displayAllMoves (VAR b: Board; c: Colour) ;
1684 displayMoves(b, m, c)
1685 END displayAllMoves ;
1692 PROCEDURE displayMovesPeg (VAR b: Board; c: Colour; peg: CARDINAL) ;
1697 displayMovesForPeg(b, m, c, peg)
1698 END displayMovesPeg ;
1705 PROCEDURE initBoard (VAR b: Board) ;
1708 b.colour[0] := SoS {} ;
1709 b.colour[1] := SoS {} ;
1712 b.home[Green] := 0 ;
1713 b.home[White] := 0 ;
1714 IF TwoPlayer OR FourPlayer
1716 homeBase[Blue] := SoS{0, 1, 2, 3,
1726 homeBase[Red] := SoS{255-0, 255-1, 255-2, 255-3,
1727 255-16, 255-17, 255-18, 255-19,
1728 255-32, 255-33, 255-34,
1737 addPiece(b, 0, Red, 1) ;
1738 addPiece(b, 1, Red, 2) ;
1739 addPiece(b, 2, Red, 3) ;
1740 addPiece(b, 3, Red, 4) ;
1741 addPiece(b, 16, Red, 5) ;
1742 addPiece(b, 17, Red, 6) ;
1743 addPiece(b, 18, Red, 7) ;
1744 addPiece(b, 19, Red, 8) ;
1745 addPiece(b, 32, Red, 9) ;
1746 addPiece(b, 33, Red, 10) ;
1747 addPiece(b, 34, Red, 11) ;
1748 addPiece(b, 48, Red, 12) ;
1749 addPiece(b, 49, Red, 13) ;
1752 addPiece(b, 255-0, Blue, 1) ;
1753 addPiece(b, 255-1, Blue, 2) ;
1754 addPiece(b, 255-2, Blue, 3) ;
1755 addPiece(b, 255-3, Blue, 4) ;
1756 addPiece(b, 255-16, Blue, 5) ;
1757 addPiece(b, 255-17, Blue, 6) ;
1758 addPiece(b, 255-18, Blue, 7) ;
1759 addPiece(b, 255-19, Blue, 8) ;
1760 addPiece(b, 255-32, Blue, 9) ;
1761 addPiece(b, 255-33, Blue, 10) ;
1762 addPiece(b, 255-34, Blue, 11) ;
1763 addPiece(b, 255-48, Blue, 12) ;
1764 addPiece(b, 255-49, Blue, 13) ;
1769 homeBase[Blue] := homeBase[Blue] + SoS{4, 20, 35, 50, 65, 64} ;
1774 homeBase[Red] := homeBase[Red] + SoS{255-4, 255-20, 255-35, 255-50, 255-65, 255-64} ;
1780 INCL(homeBase[Blue], 4) ;
1781 INCL(homeBase[Blue], 20) ;
1782 INCL(homeBase[Blue], 35) ;
1783 INCL(homeBase[Blue], 50) ;
1784 INCL(homeBase[Blue], 65) ;
1785 INCL(homeBase[Blue], 64) ;
1793 INCL(homeBase[Red], 255-4) ;
1794 INCL(homeBase[Red], 255-20) ;
1795 INCL(homeBase[Red], 255-35) ;
1796 INCL(homeBase[Red], 255-50) ;
1797 INCL(homeBase[Red], 255-65) ;
1798 INCL(homeBase[Red], 255-64) ;
1807 addPiece(b, 4, Red, 14) ;
1808 addPiece(b, 20, Red, 15) ;
1809 addPiece(b, 35, Red, 16) ;
1810 addPiece(b, 50, Red, 17) ;
1811 addPiece(b, 65, Red, 18) ;
1812 addPiece(b, 64, Red, 19) ;
1815 addPiece(b, 255-4, Blue, 14) ;
1816 addPiece(b, 255-20, Blue, 15) ;
1817 addPiece(b, 255-35, Blue, 16) ;
1818 addPiece(b, 255-50, Blue, 17) ;
1819 addPiece(b, 255-65, Blue, 18) ;
1820 addPiece(b, 255-64, Blue, 19) ;
1823 assert(b.home[Blue] = 0) ;
1824 assert(b.home[Red] = 0) ;
1825 assert(b.home[Green] = 0) ;
1826 assert(b.home[White] = 0)
1831 displayBoard - displays the board.
1834 PROCEDURE displayBoard (b: Board) ;
1838 WriteString(' a b c d e f g h i j k l m n o p') ; WriteLn ;
1839 WriteString(' +------------------------------------------------+') ; WriteLn ;
1840 FOR j := BoardY TO 1 BY -1 DO
1843 FOR i := 1 TO BoardX DO
1845 IF isColour(b, (j-1)*BoardX+(i-1), Blue)
1848 ELSIF isColour(b, (j-1)*BoardX+(i-1), Red)
1851 ELSIF isColour(b, (j-1)*BoardX+(i-1), Green)
1854 ELSIF isColour(b, (j-1)*BoardX+(i-1), White)
1866 WriteString(' +------------------------------------------------+') ; WriteLn ;
1867 WriteString(' a b c d e f g h i j k l m n o p') ; WriteLn
1875 PROCEDURE emitSpecialIf (normal, special: CHAR; i, j, x, y: CARDINAL) ;
1887 displayBoardPeg - displays the board with all moves by peg illustrated.
1890 PROCEDURE displayBoardPeg (b: Board; c: Colour; peg: CARDINAL) ;
1897 x := b.pieces[c][peg] MOD BoardX+1 ;
1898 y := b.pieces[c][peg] DIV BoardX+1 ;
1899 WriteString(' a b c d e f g h i j k l m n o p') ; WriteLn ;
1900 WriteString(' +------------------------------------------------+') ; WriteLn ;
1901 FOR j := BoardY TO 1 BY -1 DO
1904 FOR i := 1 TO BoardX DO
1906 IF isColour(b, (j-1)*BoardX+(i-1), Blue)
1908 emitSpecialIf('b', 'x', i, j, x, y)
1909 ELSIF isColour(b, (j-1)*BoardX+(i-1), Red)
1911 emitSpecialIf('r', 'x', i, j, x, y)
1912 ELSIF isColour(b, (j-1)*BoardX+(i-1), Green)
1914 emitSpecialIf('g', 'x', i, j, x, y)
1915 ELSIF isColour(b, (j-1)*BoardX+(i-1), White)
1917 emitSpecialIf('w', 'x', i, j, x, y)
1919 IF isRecorded(m, ((j-1)*BoardX)+(i-1), peg)
1923 Blue : WriteChar('B') |
1924 Red : WriteChar('R') |
1925 Green: WriteChar('G') |
1926 White: WriteChar('W')
1939 WriteString(' +------------------------------------------------+') ; WriteLn ;
1940 WriteString(' a b c d e f g h i j k l m n o p') ; WriteLn
1941 END displayBoardPeg ;
1950 * compile-command: "gm2 -g -fiso halma.mod"