;; Overall orchestration to ttt local [me you position] draw.board init if equalp :me "x [meplay 5] forever [ if already.wonp :me [print [I win!] stop] if tiedp [print [Tie game!] stop] youplay getmove ;; ask person for move if already.wonp :you [print [You win!] stop] if tiedp [print [Tie game!] stop] meplay pickmove make.triples ;; compute program's move ] end to make.triples output map "substitute.triple [123 456 789 147 258 369 159 357] end to substitute.triple :combination output map [item ? :position] :combination end to already.wonp :player output memberp (word :player :player :player) (make.triples) end to tiedp output not reduce "or map.se "numberp arraytolist :position end to youplay :square draw :you :square setitem :square :position :you end to meplay :square draw :me :square setitem :square :position :me end ;; Initialization to draw.board splitscreen clearscreen hideturtle drawline [-20 -50] 0 120 drawline [20 -50] 0 120 drawline [-60 -10] 90 120 drawline [-60 30] 90 120 end to drawline :pos :head :len penup setpos :pos setheading :head pendown forward :len end to init make "position {1 2 3 4 5 6 7 8 9} print [Do you want to play first (X)] type [or second (O)? Type X or O:] choose print [For each move, type a digit 1-9.] end to choose local "side forever [ make "side readchar pr :side if equalp :side "x [choosex stop] if equalp :side "o [chooseo stop] type [Huh? Type X or O:] ] end to chooseo make "me "x make "you "o end to choosex make "me "o make "you "x end ;; Get opponent's moves to getmove local "square forever [ type [Your move:] make "square readchar print :square if numberp :square ~ [if and (:square > 0) (:square < 10) [if freep :square [output :square]]] print [not a valid move.] ] end to freep :square output numberp item :square :position end ;; Compute program's moves to pickmove :triples local "try make "try find.win :me if not emptyp :try [output :try] make "try find.win :you if not emptyp :try [output :try] make "try find.fork if not emptyp :try [output :try] make "try find.advance if not emptyp :try [output :try] output find [memberp ? :position] [5 1 3 7 9 2 4 6 8] end to find.win :who output filter "numberp find "win.nowp :triples end to win.nowp :triple output equalp (filter [not numberp ?] :triple) (word :who :who) end to find.fork local "singles make "singles singles :me if emptyp :singles [output []] output repeated.number reduce "word :singles end to singles :who output filter [singlep ? :who] :triples end to singlep :triple :who output equalp (filter [not numberp ?] :triple) :who end to repeated.number :squares output find [memberp ? ?rest] filter "numberp :squares end to find.advance output best.move filter "numberp find [singlep ? :me] :triples end to best.move :my.single local "your.singles if emptyp :my.single [output []] make "your.singles singles :you if emptyp :your.singles [output first :my.single] ifelse (count filter [? = first :my.single] reduce "word :your.singles) > 1 ~ [output first :my.single] ~ [output last :my.single] end ;; Drawing moves on screen to draw :who :square move :square ifelse :who = "x [drawx] [drawo] end to move :square penup setpos thing word "box :square end to drawo pendown arc 360 18 end to drawx setheading 45 pendown repeat 4 [forward 25.5 back 25.5 right 90] end make "box1 [-40 50] make "box2 [0 50] make "box3 [40 50] make "box4 [-40 10] make "box5 [0 10] make "box6 [40 10] make "box7 [-40 -30] make "box8 [0 -30] make "box9 [40 -30]