BlockClosure extend [ valueWithExit [ self value: [^self] ] ]
| board showBoard patterns get message |
board := #(('.' '.' '.') ('.' '.' '.') ('.' '.' '.')) deepCopy.
showBoard := [board do: [:line | line do: [:cell | cell display]. stdout nl]].
patterns := board,
({[:i | 1]. [:i | 2]. [:i | 3]. [:i | i]. [:i | 4-i]} collect: [:spec |
(1 to: 3) collect: [:idx | (board at: idx) at: (spec value: idx)]]).
get := [:xy |
| v |
[(xy, '?') display. (v := stdin nextLine asInteger) displayNl between: 1 and: 3] whileFalse.
v
].
message := '[draw]'.
[:exit |
| rest |
[(rest := (board gather: [:line | line]) count: [:each | each = '.']) > 0] whileTrue: [
| player x y |
player := #('x' 'o') at: rest \\ 2 + 1.
('[<', player asString, '>''s turn]') displayNl.
showBoard value.
[ x := get value: 'x'.
y := get value: 'y'.
((board at: y) at: x) ~= '.'] whileTrue.
((board at: y) at: x) at: 1 put: player first.
(patterns anySatisfy: [:pat | (pat count: [:each | each = player]) = 3]) ifTrue: [
message := '[<', player asString, '> won!]'.
exit value].
]
] valueWithExit.
message displayNl.
showBoard value.
'[game over]' displayNl.
QmxvY2tDbG9zdXJlIGV4dGVuZCBbIHZhbHVlV2l0aEV4aXQgWyBzZWxmIHZhbHVlOiBbXnNlbGZdIF0gXQoKfCBib2FyZCBzaG93Qm9hcmQgcGF0dGVybnMgZ2V0IG1lc3NhZ2UgfAoKYm9hcmQgOj0gIygoJy4nICcuJyAnLicpICgnLicgJy4nICcuJykgKCcuJyAnLicgJy4nKSkgZGVlcENvcHkuCnNob3dCb2FyZCA6PSBbYm9hcmQgZG86IFs6bGluZSB8IGxpbmUgZG86IFs6Y2VsbCB8IGNlbGwgZGlzcGxheV0uIHN0ZG91dCBubF1dLgoKcGF0dGVybnMgOj0gYm9hcmQsCgkoe1s6aSB8IDFdLiBbOmkgfCAyXS4gWzppIHwgM10uIFs6aSB8IGldLiBbOmkgfCA0LWldfSBjb2xsZWN0OiBbOnNwZWMgfAoJCSgxIHRvOiAzKSBjb2xsZWN0OiBbOmlkeCB8IChib2FyZCBhdDogaWR4KSBhdDogKHNwZWMgdmFsdWU6IGlkeCldXSkuCgpnZXQgOj0gWzp4eSB8Cgl8IHYgfAoJWyh4eSwgJz8nKSBkaXNwbGF5LiAodiA6PSBzdGRpbiBuZXh0TGluZSBhc0ludGVnZXIpIGRpc3BsYXlObCBiZXR3ZWVuOiAxIGFuZDogM10gd2hpbGVGYWxzZS4gCgl2Cl0uCgptZXNzYWdlIDo9ICdbZHJhd10nLgpbOmV4aXQgfAoJfCByZXN0IHwKCVsocmVzdCA6PSAoYm9hcmQgZ2F0aGVyOiBbOmxpbmUgfCBsaW5lXSkgY291bnQ6IFs6ZWFjaCB8IGVhY2ggPSAnLiddKSA+IDBdIHdoaWxlVHJ1ZTogWwoJCXwgcGxheWVyIHggeSB8CgkJcGxheWVyIDo9ICMoJ3gnICdvJykgYXQ6IHJlc3QgXFwgMiArIDEuCgkJKCdbPCcsIHBsYXllciBhc1N0cmluZywgJz4nJ3MgdHVybl0nKSBkaXNwbGF5TmwuCgkJc2hvd0JvYXJkIHZhbHVlLgoJCVsJeCA6PSBnZXQgdmFsdWU6ICd4Jy4KCQkJeSA6PSBnZXQgdmFsdWU6ICd5Jy4KCQkJKChib2FyZCBhdDogeSkgYXQ6IHgpIH49ICcuJ10gd2hpbGVUcnVlLgoJCSgoYm9hcmQgYXQ6IHkpIGF0OiB4KSBhdDogMSBwdXQ6IHBsYXllciBmaXJzdC4KCQkocGF0dGVybnMgYW55U2F0aXNmeTogWzpwYXQgfCAocGF0IGNvdW50OiBbOmVhY2ggfCBlYWNoID0gcGxheWVyXSkgPSAzXSkgaWZUcnVlOiBbCgkJCW1lc3NhZ2UgOj0gJ1s8JywgcGxheWVyIGFzU3RyaW5nLCAnPiB3b24hXScuCgkJCWV4aXQgdmFsdWVdLgoJXQpdIHZhbHVlV2l0aEV4aXQuCm1lc3NhZ2UgZGlzcGxheU5sLgpzaG93Qm9hcmQgdmFsdWUuCidbZ2FtZSBvdmVyXScgZGlzcGxheU5sLgo=