|
| 1 | +#! /usr/local/bin/bigforth |
| 2 | +\ four in a row game |
| 3 | + |
| 4 | +6 Value #rows |
| 5 | +7 Value #cols |
| 6 | +4 Value #win |
| 7 | +#rows 2 + Value *rows |
| 8 | +#cols 2 + Value *cols |
| 9 | +8 Value #depth |
| 10 | + |
| 11 | +\ board data base |
| 12 | + |
| 13 | +Create board here *rows *cols * dup allot erase |
| 14 | + |
| 15 | +\ board operations: push stone and display result |
| 16 | + |
| 17 | +[IFUNDEF] cx@ : cx@ ( addr -- c ) c@ dup $80 and negate or ; [THEN] |
| 18 | + |
| 19 | +: b[] ( x y -- board[x,y] ) |
| 20 | + *rows * + [ board *rows 1+ + ] ALiteral + ; |
| 21 | + |
| 22 | +: .board ( -- ) cr ." -0123456" |
| 23 | + #rows 0 ?DO cr I 0 .r #cols 0 ?DO |
| 24 | + J I b[] cx@ 1 min -1 max 1+ |
| 25 | + s" x.o" drop + c@ emit LOOP LOOP ; |
| 26 | + |
| 27 | +Variable cur-stone |
| 28 | + |
| 29 | +: seeker DOES> @ ( addr index -- n ) |
| 30 | + over #win 0 ?DO over + dup cx@ cur-stone @ * 0<= ?LEAVE LOOP |
| 31 | + swap >r - negate r> / 1- ; |
| 32 | + |
| 33 | +: seek ( n -- ) Create dup , seeker Create negate , seeker ; |
| 34 | + |
| 35 | +1 seek >left >right |
| 36 | +*rows seek >up >down |
| 37 | +*rows 1- seek >lu >rd |
| 38 | +*rows 1+ seek >ld >ru |
| 39 | + |
| 40 | +: score? ( boardp -- score-addr ) |
| 41 | + >r |
| 42 | + r@ >left r@ >right + |
| 43 | + r@ >up r@ >down + max |
| 44 | + r@ >lu r@ >rd + max |
| 45 | + r@ >ld r@ >ru + max 1+ cur-stone @ * |
| 46 | + r@ c! r> ; |
| 47 | + |
| 48 | +: stone ( side col -- score-addr ) over cur-stone ! |
| 49 | + 0 swap b[] #rows 0 skip drop 1- tuck c! score? ; |
| 50 | + |
| 51 | +Variable gameover gameover on |
| 52 | + |
| 53 | +: stone? ( n col -- ) stone cx@ abs #win >= gameover ! ; |
| 54 | + |
| 55 | +\ alpha-beta min-max strategy |
| 56 | + |
| 57 | +Variable side -1 side ! |
| 58 | + |
| 59 | +: <stone ( score-addr ) 0 swap c! ; [IFDEF] macro macro [THEN] |
| 60 | +: /side side @ negate side ! ; [IFDEF] macro macro [THEN] |
| 61 | + |
| 62 | +\ count all square scores with the same sign |
| 63 | + |
| 64 | +: leaf-score ( -- score ) |
| 65 | + 0 0 board *rows *cols * bounds ?DO |
| 66 | + I cx@ dup 0>= IF dup * + ELSE swap >r dup * + r> THEN |
| 67 | + LOOP side @ 0< IF swap THEN over swap - * 8* 7 random + ; |
| 68 | + |
| 69 | +\ alpha-beta-min-max: Same evaluation for both sides; |
| 70 | +\ just negate the score of the other side |
| 71 | +\ start with minimal possible score; leave with maximal score if you win |
| 72 | +\ otherwise check score of next half-move |
| 73 | +\ leave if better than beta |
| 74 | +\ update alpha if current score is less |
| 75 | + |
| 76 | +$7FFFFFFF Constant <best> |
| 77 | +<best> negate Constant <worst> |
| 78 | +<best> 1- Constant <win> |
| 79 | +<win> negate Constant <lost> |
| 80 | +<best> 2/ 1+ Constant <half-best> |
| 81 | + |
| 82 | +Create min-max# $20 cells allot |
| 83 | + |
| 84 | +: eval-min-max ( beta n -- score best ) |
| 85 | + 1 over cells min-max# + +! |
| 86 | + dup 0= IF 2drop leaf-score 0 EXIT THEN |
| 87 | + /side -1 <worst> ( beta n best alpha ) |
| 88 | + #cols 0 ?DO |
| 89 | + 0 I b[] cx@ 0= IF |
| 90 | + side @ I stone >r |
| 91 | + r@ cx@ abs #win >= IF |
| 92 | + r> <stone 2drop I <win> LEAVE THEN |
| 93 | + 2over 1- swap >r over negate swap recurse drop |
| 94 | + dup <half-best> / - negate r> r> <stone |
| 95 | + \ beta n best alpha score beta |
| 96 | + \ if score better than beta, we are done |
| 97 | + 2dup > IF drop nip nip I swap LEAVE THEN drop |
| 98 | + \ if score better than alpha, new score is best one |
| 99 | + 2dup < IF nip nip I swap ELSE drop THEN |
| 100 | + THEN |
| 101 | + LOOP swap 2swap 2drop /side ; |
| 102 | + |
| 103 | +: c ( -- score best ) min-max# $20 cells erase |
| 104 | + -1 side ! <best> #depth eval-min-max |
| 105 | +\ min-max# #depth 1+ cells bounds ?DO I ? cell +LOOP space dup . cr |
| 106 | + 1 over stone? ; |
| 107 | + |
| 108 | +: 4init gameover off board *rows *cols * erase ; |
| 109 | + |
| 110 | +: h ( n -- ) gameover @ IF 4init cr ." New game" THEN |
| 111 | + dup #cols 0 within abort" sorry, outside the field" |
| 112 | + 0 over b[] cx@ abort" sorry, column already full" |
| 113 | + -1 swap stone? gameover @ 0= IF |
| 114 | + c drop <lost> #depth + <= IF ." I'm going to lose" |
| 115 | + ELSE gameover @ IF ." I win" THEN THEN |
| 116 | + ELSE ." you win" THEN |
| 117 | + true #cols 0 ?DO 0 I b[] cx@ 0<> and LOOP |
| 118 | + IF ." tie" gameover on THEN .board ; |
0 commit comments