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