| Bytes | Lang | Time | Link |
|---|---|---|---|
| 102 | PowerShell | 210322T184051Z | mazzy |
| 174 | PowerShell | 210322T164413Z | Wasif |
| 164 | C | 140624T215925Z | Level Ri |
| 027 | Jelly | 201224T221256Z | xigoi |
| nan | GolfScript | 140624T125005Z | Ilmari K |
| 099 | Python 2 | 140624T172819Z | Cees Tim |
| nan | 200430T013712Z | Bambelem | |
| 138 | APLNARS | 181004T162308Z | user5898 |
| 208 | Bash | 181002T232320Z | Michael |
| 258 | Bash | 140624T045309Z | Riot |
| 260 | Java 7 | 160916T070107Z | Kevin Cr |
| 036 | CJam | 141207T083741Z | Optimize |
| 110 | TSQL 2012 | 140701T215600Z | Michael |
| 056 | J 56 26? char | 140701T211655Z | algorith |
| 114 | JavaScript | 140625T135706Z | thomaux |
| 083 | J | 140629T164500Z | jpjacobs |
| 119 | Dart | 140624T100701Z | lrn |
| nan | Bash | 140625T150752Z | user1640 |
| 084 | Ruby | 140625T090520Z | Ventero |
| 085 | Ruby 2.0 | 140624T062645Z | Paul Pre |
| 169 | Haskell | 140624T062542Z | YawarRaz |
| 097 | J | 140624T154315Z | seequ |
| 146 | Haskell | 140624T142418Z | ARRG |
| 084 | Mathematica | 140624T104724Z | alephalp |
| 375 | Befunge 93 | 140624T085532Z | AndoDaan |
| 214 | Python 2 | 140624T030823Z | undergro |
PowerShell, 120 116 118 112 106 102 bytes
The more compact pattern inspired by Ventero
Fixed to follow the rule 6.
@(switch -r($args){($x='^(...)*111|^..1.1.1|1..(1|.1.)..1'){'win'}($x-replace1,0){'lose'}.{'cat'}})[0]
PowerShell, 174 bytes
param($a,$b,$c,$d,$e,$f,$g,$h,$i)@("$a$b$c","$a$d$g","$a$e$i","$b$e$h","$c$e$g","$c$f$i","$d$e$f","$g$h$i")|%{if($_-eq'XXX'){'win';exit}elseif($_-eq'OOO'){'lose';exit}};'cat'
Trivial solution.
Takes 9 parameters for each key in board.
C, 164 bytes
It's midnight here and I haven't done any testing, but I'll post the concept anyway. I'll get back to it tomorrow.
User inputs two octal numbers (I wanted to use binary but as far as I know C only supports octal):
a represents the centre square, 1 for an X, 0 for an O
b is a nine-digit number representing the perimeter squares, circling round the board starting in one corner and finishing in the same corner (with repeat of that corner only), 1 for an X, 0 for an O.
There are two possible ways to win:
centre square is X (
a=1) and two opposite squares are also X (b&b*4096is nonzero)three adjacent perimeter squares are X (
b/8 & b & b*8is nonzero.) This is only a valid win if the middle square is an edge square, not a corner square, therefore it is necessary to apply the maskmalso, to avoid the corner square cases.
Losing is detected using the variable c, which is the inverse of b.
int a,b,c,m=010101010;
main(){
scanf("%o%o",a,b);c=b^0111111111;
printf("%s",(a&&b&b*4096)|(b/8&b&b*8&m)?"win":((!a&&c&c*4096)|(c/8&c&c*8)?"lose":"cat"));
}
Jelly, 27 bytes
ŒD,ŒdḢ€;;ZEƇFṀị“ẏż“¡ṇ⁽“Zƙċ»
Input is a list of lists of ints: 0 = nothing, 1 = circle, 2 = cross.
Explanation
ŒD,ŒdḢ€;;ZEƇFṀị“ẏż“¡ṇ⁽“Zƙċ» Main monadic link
ŒD Diagonals
, Pair with
Œd Antidiagonals
Ḣ€ Head (first item) of each
[the main diagonal and antidiagonal]
; Join with the input
; Join with the input
Z zipped (transposed)
[the diagonals, rows and columns]
Ƈ Filter by
E All elements equal?
F Flatten
Ṁ Maximum
ị Index into (1-indexed)
“ẏż“¡ṇ⁽“Zƙċ» ["lose", "win", "cat"]
GolfScript, 63 + 27 = 90 bytes
I originally submitted the following 27-byte entry that exploited a loophole in the rules (as written at the time of submission) allowing a redundant input encoding:
70&.{~"win""lose"if}"cat"if
The input format for this entry is a string consisting of eight octal digits, each (redundantly) encoding three consecutive board squares:
- The first three digits each encode a single row of the board, from top down and left to right.
- The following three digits each encode a single column of the board, from left to right and top down.
- The final two digits each encode one of the diagonals (first from top left to bottom right, then from bottom left to top right).
To encode a sequence (row / column / diagonal) of three squares as an octal digit, replace every x in the sequence with a 1 and every o with a 0, and interpret the resulting sequence of ones and zeros as a binary number between 0 and 7 inclusive.
This input format is quite redundant (all board positions are encoded at least twice, with the center position encoded four times), but it does unambiguously represent any possible state of a completely filled tic-tac-toe board, and does not directly encode the winner into the input.
The input may, optionally, contain spaces or other delimiters between digits. In fact, all the program really cares about is whether or not the input string contains the digits 7 or 0.
For example, the example board:
|x|x|o|
|x|o|x|
|o|o|x|
may be represented by the input:
651 643 50
To make testing the program above easier, I also provided a 63-byte GolfScript program to convert an ASCII art board layout, as shown above, into an input string suitable for this program:
."XOxo"--[{1&!}/]:a[3/.zip"048642"{15&a=}%3/]{{2base""+}%}%" "*
This converter ignores any characters other than x and o, in either case, in its input. It produces a single digit string (complete with space delimiters as shown above) suitable for feeding into the win-determining program above, so the concatenation of these two programs can be used to determine the winner directly from the ASCII art board, and thus still qualifies as a valid entry under the current challenge rules:
."XOxo"--[{1&!}/]:a[3/.zip"048642"{15&a=}%3/]{{2base""+}%}%" "*70&.{~"win""lose"if}"cat"if
Of course, the input format converted is not particularly well optimized and the combined program could easily be golfed further. However, rather than attempting to re-golf a six-year-old solution, I prefer to just keep it as close to its originally submitted form as current rules permit.
Ps. Here's a reverse converter, just to demonstrate that the redundant input format for the 27-byte version indeed does unambiguously represent the board:
.56,48>-- 3<{2base-3>{"ox"=}%n}%"|".@@*+);
Python 2, 99 bytes
Similar to the Ruby answer:
def t(b):print['win'if w&b==w else'lose'if w&~b==w else'cat'for w in 448,56,7,292,146,73,273,84][0]
The input is the binary format described in the question: 1 for X, 0 for O, left-to-right, top-to-bottom. For example, 0b101001110 represents
XOX
OOX
XXO
which leads to output: cat
Python3 - 188 w. different approaches to evaluation
Found the subject yesterday on Reddit and was fascinated. It was a simple code just to determine victory. Same night I made up my own version. Today i found this codegolf thread. [EDIT: 'blabla'] I am amazed about the different answers and possibilities within the other languages! Nice!
188 Python3 Code
EDIT: Now Meeting Requirements 1-5
Input-Style: Player p=0/1 and board b=(1,1,1,0,1,0,1,0,1)
def t(b,p):
s,c,l,w=0,"cat","loose","win"
for i in 1,2,3,4:
u=2*i-1
a=i*i-5*i+3
if b[4]==b[4+i]==b[4-i]:
if p==b[4]:s+=1
else:s-=1
if b[u]==b[u+a]==b[u-a]:
if p==b[u]:s+=1
else:s-=1
if s>0:c=w
if s<0:c=l
return c
Victory Check Codes
103 Python3 - 'Square-Function'
EDIT1: nicer with "a==b==c==p" instead of "a+b+c==3*p"
def t(b,p):
for i in 1,2,3,4:
u=2*i-1
a=i*i-5*i+3
if b[4]==b[4+i]==b[4-i]==p or b[u]==b[u+a]==b[u-a]==p:return True
Explanation: Term for a based on f(u=1)=f(u=7)=1 f(u=3)=f(u=5)=3
113 Python3 - 'Magic Square'
Input just with board: Player is „1“, Oponent „0“
def t(b):
n=[x*y for x,y in zip(b,(2,9,4,7,5,3,6,1,8))]
if 15 in [x+y+z for x in n for y in n if x!=y for z in n if z!=x and z!=y]:return True
Explanation: Sum has to be 15 in Magic Square
119 Python3 - 'Oneliner'
inspired by Reddit Solution from user 'Xelf'
def t(b,p):
if 3*p in [b[x]+b[y]+b[z] for x,y,z in [(0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)]]:return True
@Ad Hoc Garf Hunter Thank You for the Feedback! Though i am a bit embarassed and sorry i don´t even know what 'stdin' is about. Just want to contribute some ideas and learn. Thank you for the comment on assignment and whitespaces! I just corrected it. EDIT: Hope to meet the rules now. I understand i could make spaces instead of tabs, but that just looks nasty.
APL(NARS), 69 chars, 138 bytes
{w←3 3⍴⍵⋄x←(+/1 1⍉⊖w),(+/1 1⍉w),(+⌿w),+/w⋄3∊x:'win'⋄0∊x:'lose'⋄'cat'}
The input should be one 3x3 matrix or one linear array of 9 element that can be only 1 (for X) and 0 (for O), the result will be "cat" if nobody wins, "lose" if O wins, "win" if X wins. There is no check for one invalid board or input is one array has less than 9 element or more or check each element <2.
As a comment: it would convert the input in a 3x3 matrix, and build one array named "x" where elements are the sum each row column and diagonal.
Some test see example showed from others:
f←{w←3 3⍴⍵⋄x←(+/1 1⍉⊖w),(+/1 1⍉w),(+⌿w),+/w⋄3∊x:'win'⋄0∊x:'lose'⋄'cat'}
f 1 2 3
win
f 0 0 0
lose
f 1 0 1 1 0 1 1 0 1
win
f 0 1 1 1 0 0 1 1 1
win
f 0 0 1 1 0 1 1 1 0
lose
f 1 1 0 0 1 1 1 0 0
cat
f 1 1 0 0 1 0 0 0 1
win
f 1 1 0 1 0 1 0 0 1
lose
Bash: 208 chars
y(){ tr '01' '10'<<<$@;}
f(){ x=$[($1&$2&$3)|($1&$5&$9)|($1&$4&$7)|($2&$5&$8)|($3&$5&$7)|($3&$6&$9)|($4&$5&$6)|($7&$8&$9)]; }
f $@;w=$x
f $(y $@)
([ $x -eq 1 ]&&echo lose)||([ $w -eq 1 ]&&echo win)||echo cat
To execute bash tictactoe.sh 0 1 0 1 0 1 1 0 1
Inspired by this answer.
Bash: 283 262 258
Featuring a relatively friendly interface.
t(){ sed 's/X/true/g;s/O/false/g'<<<$@;}
y(){ t $(sed 's/X/Q/g;s/O/X/g;s/Q/O/g'<<<$@);}
f(){($1&&$2&&$3)||($1&&$5&&$9)||($1&&$4&&$7)||($2&&$5&&$8)||($3&&$5&&$7)||($3&&$6&&$9)||($4&&$5&&$6)||($7&&$8&&$9)}
f $(t $@)&&echo win||(f $(y $@)&&echo lose)||echo cat
To execute bash tictactoe.sh O X O X O X X O X
Note: the list of 9 positions is a standard matrix representation. It doesn't matter if the board is represented as column major or row major, read from left to right or top to bottom - games of noughts and crosses (or tic tac toe if you insist) are symmetrical, so input order should be irrelevant to the result in every correct implementation, as long as input is linear.
Edit: Thanks to h.j.k for shorter function syntax suggestion.
Java 7, 260 bytes
String c(int[]s){int a[]=new int[8],x=0,y;for(;x<3;x++){for(y=0;y<3;a[x]+=s[x*3+y++]);for(y=0;y<3;a[x+3]+=s[y++%3]);}for(x=0;x<9;y=s[x],a[6]+=x%4<1?y:0;a[7]+=x%2<1&x>0&x++<8?y:0);x=0;for(int i:a)if(i>2)return"win";for(int i:a)if(i<1)return"loose";return"cat";}
Ungolfed & test cases:
class M{
static String c(int[] s){
int a[] = new int[8],
x = 0,
y;
for(; x < 3; x++){
for(y = 0; y < 3; a[x] += s[x * 3 + y++]);
for (y = 0; y < 3; a[x + 3] += s[y++ % 3]);
}
for(x = 0; x < 9; y = s[x],
a[6] += x % 4 < 1
? y
: 0,
a[7] += x % 2 < 1 & x > 0 & x++ < 8
? y
: 0);
x = 0;
for(int i : a){
if(i > 2){
return "win";
}
}
for(int i : a){
if(i < 1){
return "loose";
}
}
return "cat";
}
public static void main(String[] a){
/* xxo
xox
oox */
System.out.println(c(new int[]{ 1, 1, 0, 1, 0, 1, 0, 0, 1 }));
/* xxx
ooo
xxx */
System.out.println(c(new int[]{ 1, 1, 1, 0, 0, 0, 1, 1, 1 }));
/* xxo
oox
xox */
System.out.println(c(new int[]{ 1, 1, 0, 0, 0, 1, 1, 0, 1 }));
}
}
Output:
loose
win
cat
CJam, 39 38 36 characters
"ᔔꉚ굌궽渒䗠脯뗠㰍㔚귇籾〳㎪䬔쪳儏⃒ꈯ琉"2G#b129b:c~
This is a base converted code for
q3/_z__Wf%s4%\s4%]`:Q3'o*#"win"{Q'x3*#"lose""cat"?}?
which is 52 characters long.
The input is simply the string representation of the board starting from top left, going row by row. For example:
oxooxooox
which results in a win output. Or
oxooxoxox
which results in a cat output, etc.
The code simply does the following three things:
q3/_- Split the string into parts of 3, i.e. per row_z- Copy the per row array and transpose into per column array.__Wf%s4%- Reverse each row and get the left to right diagonal. This is the secondary diagonal of the board.\s4%- Get the main diagonal of the board]`- Wrap everything in array and stringify the array.
Now we have all possible groups of 3 from the board. We simply check for existence of "ooo" and "xxx" to determine the result.
T-SQL (2012),110
select max(iif(@&m=0,'lose',iif(@&m=m,'win','cat')))from(VALUES(292),(146),(73),(448),(56),(7),(273),(84))z(m)
Input is a hex number. This is pretty much a translation of the ruby solution into T-SQL pretty nice and neat.
J - 56 (26?) char
Input is given a 3x3 matrix of nine characters, because J can support that as a datatype, LOL.
(win`lose`cat{::~xxx`ooo<./@i.<"1,<"1@|:,2 7{</.,</.@|.)
Examples:
NB. 4 equivalent ways to input the example board
(3 3 $ 'xxoxoxoox') ; (_3 ]\ 'xxoxoxoox') ; ('xxo','xox',:'oox') ; (];._1 '|xxo|xox|oox')
+---+---+---+---+
|xxo|xxo|xxo|xxo|
|xox|xox|xox|xox|
|oox|oox|oox|oox|
+---+---+---+---+
(win`lose`cat{::~xxx`ooo<./@i.<"1,<"1@|:,2 7{</.,</.@|.) 3 3 $ 'xxoxoxoox'
lose
wlc =: (win`lose`cat{::~xxx`ooo<./@i.<"1,<"1@|:,2 7{</.,</.@|.)
wlc (3 3 $ 'xoxoxooxo')
cat
wlc (3 3 $ 'xxxoooxxx')
win
If we are allowed the Golfscriptish encoding of octal digits redundantly representing the state of each row, column, and diagonal, then it's just 26 characters:
win`lose`cat{::~7 0<./@i.] 6 5 1 6 4 3 5 0
lose
f=:win`lose`cat{::~7 0<./@i.]
f 7 0 7 5 5 5 5 5
win
JavaScript, 133, 114 characters
r = '/(1){3}|(1.{3}){2}1|(1.{4}){2}1|(1\|.1.\|1)/';alert(i.match(r)?'WIN':i.match(r.replace(/1/g,0))?'LOSS':'CAT')
The input i is a simple string with delimiters for the rows, i.e. 100|001|100
Edit: updated my method to replace the 1s in the regex with zeroes to check for the loss case.
J : 83
(;:'lose cat win'){::~>:*(-&(+/@:(*./"1)@;@(;((<0 1)&|:&.>@(;|.)(,<)|:)))-.)3 3$'x'=
Usage: just append a string of x's and o's and watch the magic work. eg. 'xxxoooxxx'.
The inner verb (+/@:(*./"1)@;@(;((<0 1)&|:&.>@(;|.)(,<)|:))) basically boxes together the original binary matrix, with the transpose boxed together with the 2 diagonals.
These results are razed together ; row sums are taken to determine wins, and then summed. further I'll call this verb Inner.
For finding the winner, the difference of the scores between the normal and inversed binary matrices is taken by the hook (-&Inner -.).
The rest of the code simply makes the outputs, and selects the right one.
Dart - 119
(See dartlang.org).
Original version using RegExp: 151 chars.
main(b,{w:"cat",i,p,z}){
for(p in["olose","xwin"])
for(i in[0,2,3,4])
if(b[0].contains(new RegExp('${z=p[0]}(${'.'*i}$z){2}')))
w=p.substring(1);
print(w);
}
Input on the command line is 11 characters, e.g., "xxx|ooo|xxx". Any non-xo character can be used as delimiter.
Leading whitespace and newlines should be omitted before counting characters, but I cut away the internal whitespace where possible. I wish there was a smaller way to make the substring.
Recusive bit-base version: 119 chars. Input must be a 9-bit number with 1s representing 'x' and 0s representing 'o'.
main(n){
n=int.parse(n[0]);
z(b,r)=>b>0?b&n==b&511?"win":z(b>>9,n&b==0?"lose":r):r;
print(z(0x9224893c01c01e2254,"cat"));
}
Bash, 107 103
Generates and runs a sed script.
I/O format: oxo-oox-xoo outputs lose (use a - to separate rows). Input on stdin.
Requires GNU sed for the c command.
I've interpreted rule 5 as "if both win and lose are possible, choose win".
Main Code
This is the actual answer.
Nothing interesting really. It defines $b as /cwin to save characters, then defines the win condition part of the script, then uses sed y/x/o/\;s$b/close/ to convert x to o and cwin to close (thereby generating the lose conditions). It then sends the two things and ccat (which will output cat if no win/lose condition is matched) to sed.
b=/cwin
v="/xxx$b
/x...x...x$b
/x..-.x.-..x$b
/x-.x.-x$b"
sed "$v
`sed y/x/o/\;s$b/close/<<<"$v"`
ccat"
Generated Code
This is the sed script generated and run by the Bash script.
In the regexes, . matches any character and after them cTEXT prints TEXT and exits if the regex is matched.
This can run as a standalone sed script. It's 125 characters long, you can count it as another solution.
/xxx/cwin
/x...x...x/cwin
/x..-.x.-..x/cwin
/x-.x.-x/cwin
/ooo/close
/o...o...o/close
/o..-.o.-..o/close
/o-.o.-o/close
ccat
Ruby, 84 characters
$><<(gets.tr("01","10")[r=/0..(0|.0.)..0|000(...)*$|^..0.0.0/]?:win:~r ?:lose: :cat)
Simple, RegExp based solution. The input format is a 9-digit binary string, e.g. 110101001 for the example board given in the question.
Ruby, 78 characters
$><<(gets.tr("ox","xo")[r=/o...(o|.o.)...o|ooo|o_.o._o/]?:win:~r ?:lose: :cat)
Input format: xxo_xox_oox
Ruby 2.0, 85 characters
Here's a simple bitmask-based solution in Ruby:
d=gets.hex
$><<[292,146,73,448,56,7,273,84].map{|m|d&m<1?:lose:d&m<m ?:cat: :win}.max
The board is represented as a hex number, made up of nine bits corresponding to the nine squares. 1 is an X, 0 is an O. This is just like the 0x1a9 example in the question, though the 0x is optional!
There's probably a better way to do the bitmasks then just hardcoding a big list. I'll happily take suggestions.
See it running on Ideone here.
Haskell, 169
main=interact$(\x->last$"cat":[b|(a,b)<-[("ooo","lose"),("xxx","win")],any(==a)x]).(\x->x++(foldr(zipWith(:))(repeat[])x)++map(zipWith(!!)x)[[0..],[2,1,0]]).take 3.lines
Input format: "X" is represented only by x, "O" only by o. Within each row, characters are simultaneous without spaces, etc. Rows are separated by new lines.
Generates all possible rows/columns/diagonals, then filters [("ooo","lose"),("xxx","win")] by their existence on the board, then selects the second word in the tuple, so we know which players won. We prepend "cat" so that we can take the last element of the list as our winner. If both players won, "win" will be last (list comprehensions maintain order). Since "cat" is always first, if a winner exists, it will be chosen, but otherwise a last element still exists as prepending "cat" guarantees nonemptyness.
EDIT: Shaved 3 characters by changing last list comprehension to map.
J - 97 bytes
Well, the simplest approach available. The input is taken as 111222333, where the numbers represent rows. Read left-to-right. Player is x and enemy is o. Empty squares can be anything except x or o.
f=:(cat`lose>@{~'ooo'&c)`('win'"_)@.('xxx'&c=:+./@(r,(r|:),((r=:-:"1)(0 4 8&{,:2 4 6&{)@,))3 3&$)
Examples: (NB. is a comment)
f 'xoxxoxxox' NB. Victory from first and last column.
win
f 'oxxxooxxx' NB. Victory from last row.
win
f 'ooxxoxxxo' NB. The example case, lost to a diagonal.
lose
f 'xxooxxxoo' NB. Nobody won.
cat
f 'xoo xx ox' NB. Victory from diagonal.
win
Ungolfed code an explanation
row =: -:"1 Checks if victory can be achieved from any row.
col =: -:"1 |: Checks if victory can be achieved from any column.
diag =: -:"1 (0 4 8&{ ,: 2 4 6&{)@, Checks if victory can be achieved from diagonals.
check =: +./@(row,col,diag) 3 3&$ Checks all of the above and OR's them.
f =: (cat`lose >@{~ 'ooo'&check)`('win'"_)@.('xxx'&check)
Check if you have won ........................@.('xxx'&check)
If yes, return 'win' .............. ('win'"_)
If not (cat`lose >@{~ 'ooo'&check)
Check if enemy won ................... 'ooo'&check
If yes, return 'lose' ---`lose >@{~
If not, return 'cat' cat`---- >@{~
Haskell, 146 chars
To make things interesting, you get to determine your input structure for the state- which you must then explain.
OK :). My representation of a board is one of those 126 characters
ĻŃŇʼnŊœŗřŚşšŢťŦŨųŷŹźſƁƂƅƆƈƏƑƒƕƖƘƝƞƠƤƳƷƹƺƿǁǂDždžLjǏǑǒǕǖǘǝǞǠǤǯDZDzǵǶǸǽǾȀȄȍȎȐȔȜȳȷȹȺȿɁɂɅɆɈɏɑɒɕɖɘɝɞɠɤɯɱɲɵɶɸɽɾʀʄʍʎʐʔʜʯʱʲʵʶʸʽʾˀ˄ˍˎː˔˜˭ˮ˰˴˼̌
Here's the solution in 146 chars :
main=interact$(\x->case(head x)of h|elem h "ĻŃœťŦŨųŷŹƁƂƅƈƕƠƤƳƿǂdžǞǤǵǾȀȳȿɁɅɑɒɘɝɠɤɵɽʀʐʽʾː˭ˮ˰˴˼̌"->"lose";h|elem h "ƏƝƞƹǁLjǑǝȍȺɆɈɶɾʎʸ"->"cat";h->"win")
And here's how it works, as an haskell script :
import Data.List (subsequences, (\\))
import Data.Char (chr)
-- A set of indexes [0-8] describing where on the board pieces of a single color have been played
-- For example the board "OxO;Oxx;xxO" is indexes [0,2,3,8]
type Play = [Int]
-- There are 126 filled tic tac toe boards when X plays first.
-- (This is a combination of 4 OHs among 9 places : binomial(9 4) = 126)
-- perms returns a list of all such possible boards (represented by the index of their OHs).
perms = filter (\x -> 4 == length x) $ subsequences [0..8]
-- We now create an encoding for plays that brings them down to a single char.
-- The index list can be seen as an 9 bit binary word [0,2,3,8] -> '100001101'
-- This, in turn is the integer 269. The possible boards give integers between 15 and 480.
-- Let's call those PlayInts
type PlayInt = Int
permToInt [] = 0
permToInt (x:xs) = (2 ^ x) + permToInt xs
-- Since the characters in the range 15-480 are not all printable. We offset the chars by 300, this gives the range
-- ĻŃŇʼnŊœŗřŚşšŢťŦŨųŷŹźſƁƂƅƆƈƏƑƒƕƖƘƝƞƠƤƳƷƹƺƿǁǂDždžLjǏǑǒǕǖǘǝǞǠǤǯDZDzǵǶǸǽǾȀȄȍȎȐȔȜȳȷȹȺȿɁɂɅɆɈɏɑɒɕɖɘɝɞɠɤɯɱɲɵɶɸɽɾʀʄʍʎʐʔʜʯʱʲʵʶʸʽʾˀ˄ˍˎː˔˜˭ˮ˰˴˼̌
-- Of all distinct, printable characters
uOffset = 300
-- Transform a PlayInt to its Char representation
pIntToUnicode i = chr $ i + uOffset
-- Helper function to convert a board in a more user friendly representation to its Char
-- This accepts a representation in the form "xooxxxoxo"
convertBoard s = let play = map snd $ filter (\(c, i) -> c == 'o') $ (zip s [0..]) :: Play
in pIntToUnicode $ permToInt play
--
-- Now let's cook some data for our final result
--
-- All boards as chars
allUnicode = let allInts = map permToInt perms
in map pIntToUnicode allInts
-- Now let's determine which boards give which outcome.
-- These are all lines, columns, and diags that give a win when filled
wins = [
[0,1,2],[3,4,5],[6,7,8], -- lines
[0,3,6],[1,4,7],[2,5,8], -- columns
[0,4,8],[2,4,6] -- diagonals
]
isWin :: Play -> Bool
isWin ps = let triplets = filter (\x -> 3 == length x) $ subsequences ps -- extract all triplets in the 4 or 5 moves played
in any (\t -> t `elem` wins) triplets -- And check if any is a win line
-- These are OH wins
oWins = filter isWin perms
-- EX wins when the complement board wins
xWins = filter (isWin . complement) perms
where complement ps = [0..9] \\ ps
-- And it's stalemate otherwise
cWins = (perms \\ oWins) \\ xWins
-- Write the cooked data to files
cookData = let toString = map (pIntToUnicode . permToInt) in do
writeFile "all.txt" allUnicode
writeFile "cWins.txt" $ toString cWins
writeFile "oWins.txt" $ toString oWins
writeFile "xWins.txt" $ toString xWins
-- Now we know that there are 48 OH-wins, 16 stalemates, and 62 EX wins (they have more because they play 5 times instead of 4).
-- Finding the solution is just checking to which set an input board belongs to (ungolfed :)
main = interact $ \x -> case (head x) of -- Only consider the first input char
h | elem h "ĻŃœťŦŨųŷŹƁƂƅƈƕƠƤƳƿǂdžǞǤǵǾȀȳȿɁɅɑɒɘɝɠɤɵɽʀʐʽʾː˭ˮ˰˴˼̌" -> "lose" -- This string is == oWins
h | elem h "ƏƝƞƹǁLjǑǝȍȺɆɈɶɾʎʸ" -> "cat" -- And this one == cWins
h -> "win"
Mathematica, 84 chars
a=Input[];Which[Max@#>2,win,Min@#<1,lose,1>0,cat]&@{Tr@a,Tr@Reverse@a,Tr/@a,Total@a}
Input format: {{1, 1, 0}, {1, 0, 1}, {0, 0, 1}}
Befunge 93 - 375
Takes a binary string as input.
99>~\1-:!!|>v
>0v>v>v >^$>v
^+ + + 0<:p:
>#+#+#+ ^246
^+ + + 0<265
>#+#+#+ ^pp6
^+ + + 0<2++
#+#+#+ 55p
0 0 552
>^>^>0v +46
v+ + + < ppp
>0 + + + v 444
v!!-3:<< 246
v_"ni"v ppp
0v" w"< :+:
\>,,,,@ 266
->,,,@ 555
!^"cat"_^ 645
!>:9-! ^ +:+
>| p:p
>"eso"v 6p6
@,,,,"l"< 246
p2p
>^
v <^ <
Reads the string. Bruteforce writes it (the right most vertical strip) as a matrix in between the
^+ + +
>#+#+#+
^+ + +
>#+#+#+
^+ + +
#+#+#+
adding lattice (idk). Determins the sum of the columns, rows, and two diagnals. Compares those values to 3 ("win") or 0 ("lose"), else if all the values equal 1 or 2 then draw ("cat").
Python 2 - 214 bytes
b=eval(raw_input())
s=map(sum,b)
w,l='win','lose'
e="if min(s)<1:print l;a\nif max(s)>2:print w;a"
exec e+'\ns=map(sum,zip(*b))\n'+e
m=b[1][1]
for i in 0,2:
if m==b[0][i]==b[2][abs(i-2)]:print[l,w][m];a
print'cat'
I'm sure there are improvements to be made.
To run:
python2 tictactoe.py <<< '[[1,1,1],[1,0,1],[0,1,0]]'
which represents this board:
X|X|X
-----
X|O|X
-----
0|X|0
Exits with a NameError exception in every case except cat.