Код Гольф: Крестики-Нолики

разместите свой самый короткий код по количеству символов, чтобы проверить, выиграл ли игрок, и если да, то какой.

Предположим, у вас есть целочисленный массив в переменной b (доска), которая держит доску Tic Tac Toe, и ходы игроков, где:

  • 0 = Нет
  • 1 = Игрок 1 (X)
  • 2 = Игрок 2 (O)

Итак, учитывая массив b = [ 1, 2, 1, 0, 1, 2, 1, 0, 2 ] будет представлять Совет

X|O|X
-+-+-
 |X|O
-+-+-
X| |O

для этого ситуация, ваш код должен вывести 1 чтобы указать, что игрок 1 выиграл. Если никто не выиграл, Вы можете вывести 0 или false.

мое собственное (Ruby) решение будет скоро.

редактировать: Извините, забыл отметить его как сообщество wiki. Вы можете предположить, что вход хорошо сформирован и не должен быть проверен на ошибку.


обновление: пожалуйста, разместите свое решение в виде функции. Большинство людей уже сделали это, но некоторые нет, что не совсем справедливо. Доска поставлена к вашей функции как параметр. Результат должен быть возвращен функцией. Функция может иметь имя по вашему выбору.

30 ответов


C, 77 (83) символов

Это вариант dmckeeрешение, за исключением того, что каждая пара цифр в компактном кодировании теперь является базой-9 цифр символов ASCII.

на 77-char версия, не работает на MSVC:

// "J)9\t8\r=," == 82,45,63,10,62,14,67,48,00 in base 9.
char*k="J)9 8\r=,",c;f(int*b){return(c=*k++)?b[c/9]&b[c%9]&b[*k--%9]|f(b):0;}

этой 83-char версия, должна работать на каждом компиляторе C:

f(int*b){char*k="J)9    8\r=,",s=0,c;while(c=*k++)s|=b[c%9]&b[c/9]&b[*k%9];return s;}

(обратите внимание, что пробелы между 9 и 8 должны быть вкладкой. Сайте StackOverflow преобразует все вкладки в пробелы.)


тест:

#include <stdio.h>  
void check(int* b) {
    int h0 = b[0]&b[1]&b[2];
    int h1 = b[3]&b[4]&b[5];
    int h2 = b[6]&b[7]&b[8];
    int h3 = b[0]&b[3]&b[6];
    int h4 = b[1]&b[4]&b[7];
    int h5 = b[2]&b[5]&b[8];
    int h6 = b[0]&b[4]&b[8];
    int h7 = b[2]&b[4]&b[6];
    int res = h0|h1|h2|h3|h4|h5|h6|h7;
    int value = f(b);
    if (value != res)
        printf("Assuming f({%d,%d,%d, %d,%d,%d, %d,%d,%d}) == %d; got %d instead.\n", 
            b[0],b[1],b[2], b[3],b[4],b[5], b[6],b[7],b[8], res, value);
}
#define MAKEFOR(i) for(b[(i)]=0;b[(i)]<=2;++b[(i)])

int main() {
    int b[9];

    MAKEFOR(0)
    MAKEFOR(1)
    MAKEFOR(2)
    MAKEFOR(3)
    MAKEFOR(4)
    MAKEFOR(5)
    MAKEFOR(6)
    MAKEFOR(7)
    MAKEFOR(8)
        check(b);

    return 0;
}

сумасшедшее решение Python-79 символов

max([b[x] for x in range(9) for y in range(x) for z in range(y)
    if x+y+z==12 and b[x]==b[y]==b[z]] + [0])

однако это предполагает другой порядок для позиций доски в b:

 5 | 0 | 7
---+---+---
 6 | 4 | 2
---+---+---
 1 | 8 | 3

то есть b[5] представляет верхний левый угол, и так далее.

чтобы свести к минимуму выше:

r=range
max([b[x]for x in r(9)for y in r(x)for z in r(y)if x+y+z==12and b[x]==b[y]==b[z]]+[0])

93 символа и новая строка.

обновление: до 79 символов и новой строки с помощью побитового и трюк:

r=range
max([b[x]&b[y]&b[z]for x in r(9)for y in r(x)for z in r(y)if x+y+z==12])

Python 80 (69) char

не самое короткое решение Python, но мне нравится, как он вводит "кости"в игру Крестики-нолики:

W=lambda b:max([b[c/5-9]&b[c/5+c%5-9]&b[c/5-c%5-9]for c in map(ord,"DICE>3BQ")])

69 символов для более простого выражения:

max([b[c/5-9]&b[c/5+c%5-9]&b[c/5-c%5-9]for c in map(ord,"DICE>3BQ")])

Perl, 87 85 символы

функция, которая возвращает 0, 1 или 2, используя регулярное выражение, конечно (символ перевода строки, чтобы избежать полосы прокрутки):

sub V{$"='';$x='(1|2)';"@_"=~
/^(...)*$x|^..$x..|$x....|$x....../?$^N:0}

его можно назвать как V(@b), например.


J, 50 символов

w=:3 : '{.>:I.+./"1*./"1]1 2=/y{~2 4 6,0 4 8,i,|:i=.i.3 3'

Я не доволен повторением себя (горизонтально / вертикально и диагонали), но я думаю, что это справедливое начало.

C# w / LINQ:

public static int GetVictor(int[] b)
{
    var r = Enumerable.Range(0, 3);
    return r.Select(i => r.Aggregate(3, (s, j) => s & b[i * 3 + j])).Concat(
        r.Select(i => r.Aggregate(3, (s, j) => s & b[j * 3 + i]))).Aggregate(
        r.Aggregate(3, (s, i) => s & b[i * 3 + i]) | r.Aggregate(3, (s, i) => s & b[i * 3 + (2 - i)]),
        (s, i) => s | i);
}

Стратегия: Побитовое AND каждый элемент строки / столбца / диагонали с другими элементами (с 3 в качестве семени) для получения победителя для этого подмножества и OR их все вместе в конце.


Рубин, 115 символов

Упс: как-то я много просчитался. Это на самом деле 115 символов, а не 79.

def t(b)[1,2].find{|p|[448,56,7,292,146,73,273,84].any?{|k|(k^b.inject(0){|m,i|m*2+((i==p)?1:0)})&k==0}}||false end

# Usage:
b = [ 1, 2, 1,
      0, 1, 2,
      1, 0, 2 ]
t(b) # => 1

b = [ 1, 1, 0,
      2, 2, 2,
      0, 2, 1 ]
t(b) # => 2

b = [ 0, 0, 1,
      2, 2, 0,
      0, 1, 1 ]
t(b) # => false

и расширенный код, для образовательных целей:

def tic(board)
  # all the winning board positions for a player as bitmasks
  wins = [ 0b111_000_000,  # 448
           0b000_111_000,  #  56
           0b000_000_111,  #   7
           0b100_100_100,  # 292
           0b010_010_010,  # 146
           0b001_001_001,  #  73
           0b100_010_001,  # 273
           0b001_010_100 ] #  84

  [1, 2].find do |player| # find the player who's won
    # for the winning player, one of the win positions will be true for :
    wins.any? do |win|
      # make a bitmask from the current player's moves
      moves = board.inject(0) { |acc, square|
        # shift it to the left and add one if this square matches the player number
        (acc * 2) + ((square == player) ? 1 : 0)
      }
      # some logic evaluates to 0 if the moves match the win mask
      (win ^ moves) & win == 0
    end
  end || false # return false if the find returns nil (no winner)
end

Я уверен, что это может быть сокращено, особенно большой массив и, возможно, код для получения битовой маски ходов игроков-это троичное меня беспокоит-но я думаю, что это довольно хорошо на данный момент.


Perl, 76 char

sub W{$n=$u=0;map{$n++;$u|=$_[$_-$n]&$_[$_]&$_[$_+$n]for/./g}147,4,345,4;$u}

есть три способа выиграть по горизонтали:

0,1,2   ==>   1-1, 1, 1+1
3,4,5   ==>   4-1, 4, 4+1
6,7,8   ==>   7-1, 7, 7+1

один способ выиграть по диагонали от нижнего левого до верхнего правого:

2,4,6   ==>   4-2, 4, 4+2

три способа выиграть по вертикали:

0,3,6   ==>   3-3, 3, 3+3
1,4,7   ==>   4-3, 4, 4+3
2,5,8   ==>   5-3, 5, 5+3

один из способов выиграть по диагонали от верхнего левого до нижнего правого:

0,4,8   ==>   4-4, 4, 4+4

прочитайте средние столбцы, чтобы получить магические числа.


Октава / Matlab, 97 символов, включая пробелы и новые строки. Выходы 0, если нет победителя, 1, если игрок 1 выиграл, 2, если игрок 2 выиграл, и 2.0801, если оба игрока "выиграли":

function r=d(b)
a=reshape(b,3,3)
s=prod([diag(a) diag(fliplr(a)) a a'])
r=sum(s(s==1|s==8))^(1/3)

если мы изменим спецификацию и передадим b как матрицу 3x3 с самого начала, мы можем удалить строку reshape, сократив ее до 80 символов.


потому что никто не выигрывает в tictactoe при правильной игре, я думаю, что это самый короткий код

echo 0; 

7 символов

Update: лучшей записью для bash было бы следующее:

86 характеров или 81 исключая определения функции (выигрыша ()).

win()for q in 1 28 55 3 12 21 4 20;{ [[ 3*w -eq B[f=q/8]+B[g=q%8]+B[g+g-f] ]]&&break;}

но это код от программы tic-tac-toe в bash, поэтому он не совсем соответствует спецификации.

# player is passed in caller's w variable. I use O=0 and X=2 and empty=8 or 9
# if a winner is found, last result is true (and loop halts) else false
# since biggest test position is 7 I'll use base 8. could use 9 as well but 10 adds 2 characters to code length
# test cases are integers made from first 2 positions of each row
# eg. first row (0 1 2) is 0*8+1 = 1
# eg. diagonal (2 4 6) is 2*8+4 = 20
# to convert test cases to board positions use X/8, X%8, and X%8+(X%8-X/8)
# for each test case, test that sum of each tuplet is 3*player value

Рубин, 85 чар

def X(b)
u=0
[2,6,7,8,9,13,21,-9].each do|c|u|=b[n=c/5+3]&b[n+c%5]&b[n-c%5]end
u
end

если на входе оба игрока побеждают, например,

     X | O | X
    ---+---+---
     X | O | O
    ---+---+---
     X | O | X

затем выход 3.


Haskell, предполагая магические квадраты выше. 77 символов

77 исключает импорт и определение b.

import Data.Bits
import Data.Array

b = listArray (0,8) [2,1,0,1,1,1,2,2,0]
w b = maximum[b!x.&.b!y.&.b!z|x<-[0..8],y<-[x+1..8],z<-[12-x-y],z<8,z>=0,z/=y]

или 82 предполагая нормальный заказ:

{-# LANGUAGE NoMonomorphismRestriction #-}
import Data.Bits
import Data.Array

b = listArray (0,8) [1,2,1,0,1,2,1,0,2]
w b = maximum[b!x.&.b!y.&.b!z|x<-[0..8],d<-[1..4],y<-[x+d],z<-[y+d],d/=2||x==2,z<9]

C, 99 символов

не победитель, но, возможно, есть номер для улучшения. Никогда не делал этого раньше. Оригинальная концепция, первый проект.

#define l w|=*b&b[s]&b[2*s];b+=3/s;s
f(int*b){int s=4,w=0;l=3;l;l;l=2;--b;l=1;b-=3;l;l;return l;}

спасибо KennyTM для немного идей и проводки испытания.

"версия развития":

#define l w|=*b&b[s]&b[2*s];b+=3/s;s // check one possible win
f( int *b ) {
        int s=4,w=0; // s = stride, w = winner
        l=3;     // check stride 4 and set to 3
        l;l;l=2; // check stride 3, set to 2
        --b;l=1; // check stride 2, set to 1
        b-=3;l;l; return l; // check stride 1
}

(Железный)питон, 75 символов

75 символов для полной функции

T=lambda a:max(a[b/6]&a[b/6+b%6]&a[b/6+b%6*2]for b in[1,3,4,9,14,15,19,37])

66 символов, если вы опустите определение функции, как это сделали некоторые другие

r=max(a[b/6]&a[b/6+b%6]&a[b/6+b%6*2]for b in[1,3,4,9,14,15,19,37])

8 различных направлений представлены начальным значением + incrementor, сжатым в одно число, которое может быть извлечено с помощью деления и модуля. Например 2,5,8 = 2*6 + 3 = 15.

проверка того, что строка содержит три равных значения, выполняется с помощью & оператор. (что приводит к нулю, если они не равны). max используется для поиска возможного победителя.


решение в C (162 символов):

Это использует тот факт, что одно значение игрока (1) и два значения игрока (2) имеют независимые биты. Таким образом можно побитовое и значения трех тестовых полей вместе-- если значение ненулевое, то все три значения должны быть одинаковыми. Кроме того, полученное значение == игрок, который выиграл.

не самое короткое решение до сих пор, но лучшее, что я мог сделать:

void fn(){
    int L[]={1,0,1,3,1,6,3,0,3,1,3,2,4,0,2,2,0};
    int s,t,p,j,i=0;
    while (s=L[i++]){
        p=L[i++],t=3;
        for(j=0;j<3;p+=s,j++)t&=b[p];
        if(t)putc(t+'0',stdout);}
}

A больше читаемая версия:

void fn2(void)
{
    // Lines[] defines the 8 lines that must be tested
    //  The first value is the "Skip Count" for forming the line
    //  The second value is the starting position for the line
    int Lines[] = { 1,0, 1,3, 1,6, 3,0, 3,1, 3,2, 4,0, 2,2, 0 };

    int Skip, Test, Pos, j, i = 0;
    while (Skip = Lines[i++])
    {
        Pos = Lines[i++];   // get starting position
        Test = 3;           // pre-set to 0x03 (player 1 & 2 values bitwise OR'd together)

        // search each of the three boxes in this line
        for (j = 0; j < 3; Pos+= Skip, j++)
        {
            // Bitwise AND the square with the previous value
            //  We make use of the fact that player 1 is 0x01 and 2 is 0x02
            //  Therefore, if any bits are set in the result, it must be all 1's or all 2's
            Test &= b[Pos];
        }

        // All three squares same (and non-zero)?
        if (Test)
            putc(Test+'0',stdout);
    }
}

Python, 102 символа

поскольку вы на самом деле не указали, как получить вход и выход, это "сырая" версия, которая, возможно, должна быть обернута в функцию. b список ввода; r выход (0, 1 или 2).

r=0
for a,c in zip("03601202","11133342"):s=set(b[int(a):9:int(c)][:3]);q=s.pop();r=r if s or r else q

Lua, 130 символов

130 символов-это только размер функции. Функция ничего не возвращает, если совпадение не найдено, что в Lua аналогично возвращению false.

function f(t)z={7,1,4,1,1,3,2,3,3}for b=1,#z-1 do
i=z[b]x=t[i]n=z[b+1]if 0<x and x==t[i+n]and x==t[i+n+n]then
return x end end end

assert(f{1,2,1,0,1,2,1,0,2}==1)
assert(f{1,2,1,0,0,2,1,0,2}==nil)
assert(f{1,1,2,0,1,2,1,0,2}==2)
assert(f{2,1,2,1,2,1,2,1,2}==2)
assert(f{2,1,2,1,0,2,2,2,1}==nil)
assert(f{1,2,0,1,2,0,1,2,0}~=nil)
assert(f{0,2,0,0,2,0,0,2,0}==2)
assert(f{0,2,2,0,0,0,0,2,0}==nil)

assert(f{0,0,0,0,0,0,0,0,0}==nil)
assert(f{1,1,1,0,0,0,0,0,0}==1)
assert(f{0,0,0,1,1,1,0,0,0}==1)
assert(f{0,0,0,0,0,0,1,1,1}==1)
assert(f{1,0,0,1,0,0,1,0,0}==1)
assert(f{0,1,0,0,1,0,0,1,0}==1)
assert(f{0,0,1,0,0,1,0,0,1}==1)
assert(f{1,0,0,0,1,0,0,0,1}==1)
assert(f{0,0,1,0,1,0,1,0,0}==1)

Visual Basic 275 254 (со свободным набором) символов

 Function W(ByVal b())

    Dim r

    For p = 1 To 2

            If b(0) = b(1) = b(2) = p Then r = p
            If b(3) = b(4) = b(5) = p Then r = p
            If b(6) = b(7) = b(8) = p Then r = p
            If b(0) = b(3) = b(6) = p Then r = p
            If b(1) = b(4) = b(7) = p Then r = p
            If b(2) = b(5) = b(8) = p Then r = p
            If b(0) = b(4) = b(8) = p Then r = p
            If b(6) = b(4) = b(2) = p Then r = p

    Next

    Return r

End Function

JavaScript-функция " w " ниже 114 символов

<html>   
<body>
<script type="text/javascript">

var t = [0,0,2,0,2,0,2,0,0];

function w(b){
    i = '012345678036147258048642';
    for (l=0;l<=21;l+=3){
        v = b[i[l]];
        if (v == b[i[l+1]]) if (v == b[i[l+2]]) return v;   
    }
}

alert(w(t));

</script>
</body>
</html>

J, 97 символов.

1+1 i.~,+./"2>>(0 4 8,2 4 6,(],|:)3 3$i.9)&(e.~)&.>&.>(]<@:#"1~[:#:[:i.2^#)&.>(I.@(1&=);I.@(2&=))

Я планировал разместить объяснение того, как это работает, но это было вчера и теперь я не могу читать этот код.

идея состоит в том, что мы создаем список всех возможных выигрышных троек (048,246,012,345,678,036,147,258), затем делаем powerset квадратов каждого игрока, а затем пересекаем два списка. Если есть матч, то это победитель.


Python-75 символов (64)

Я придумал 2 выражения, каждый 64chars:

max(a[c/8]&a[c/8+c%8]&a[c/8-c%8]for c in map(ord,'\t$#"!+9'))

и

max(a[c/5]&a[c/5+c%5]&a[c/5+c%5*2]for c in[1,3,4,8,12,13,16,31])

когда вы добавляете " W=lambda b:", чтобы сделать его функцией, это составляет 75chars. Самый короткий Python до сих пор?


Python, 285 байт

b,p,q,r=["."]*9,"1","2",range
while"."in b:
 w=[b[i*3:i*3+3]for i in r(3)]+[b[i::3]for i in r(3)]+[b[::4],b[2:8:2]]
 for i in w[:3]:print i
 if["o"]*3 in w or["x"]*3 in w:exit(q)
 while 1:
  m=map(lambda x:x%3-x+x%3+7,r(9)).index(input())
  if"."==b[m]:b[m]=".xo"[int(p)];p,q=q,p;break

...О, это не то, что вы имели в виду, когда сказали "код Гольф: крестики-нолики"? ;) (введите номера numpad для размещения x или o, т. е. 7-Северо-Запад)

Текст

board = ["."]*9   # the board
currentname = "1" # the current player
othername = "2"   # the other player

numpad_dict = {7:0, 8:1, 9:2, # the lambda function really does this!
               4:3, 5:4, 6:5,
               1:6, 2:7, 3:8}

while "." in board:
    # Create an array of possible wins: horizontal, vertical, diagonal
    wins = [board[i*3:i*3+3] for i in range(3)] + \ # horizontal
           [board[i::3]      for i in range(3)] + \ # vertical
           [board[::4], board[2:8:2]]               # diagonal

    for i in wins[:3]: # wins contains the horizontals first,
        print i        # so we use it to print the current board

    if ["o"]*3 in wins or ["x"]*3 in wins: # somebody won!
        exit(othername)                    # print the name of the winner
                                           # (we changed player), and exit
    while True: # wait for the player to make a valid move
        position = numpad_dict[input()] 
        if board[position] == ".": # still empty -> change board
            if currentname == "1":
                board[position] = "x"
            else:
                board[position] = "o"
            currentname, othername = othername, currentname # swap values

Я уверен, что есть более короткий способ сделать это, но... Perl, 141 символ (134 внутри функции)

sub t{$r=0;@b=@_;@w=map{[split//]}split/,/,"012,345,678,036,147,258,048,246";for(@w){@z=map{$b[$_]}@$_;$r=$z[0]if!grep{!$_||$_!=$z[0]}@z;}$r;}

c -- 144 символа

Minified:

#define A(x) a[b[x%16]]
int c,b[]={4,8,0,1,2,4,6,0,3,4,5,2,8,6,7,2};int
T(int*a){for(c=0;c<16;c+=2)if(A(c)&A(c+1)&A(c+2))return A(c);return 0;}

оба возвращают счетчик (один необходимый, а другой нуждается в замене пробелом).

коды массива для восьми способов выиграть в триплетах, начиная с четных позиций и взятых mod 16.

побитовые и трюк украден из Эрик Пи.


более читабельном виде:

#define A(x) a[b[x%16]]

// Compact coding of the ways to win.
//
// Each possible was starts a position N*2 and runs through N*2+2 all
// taken mod 16
int c,b[]={4,8,0,1,2,4,6,0,3,4,5,2,8,6,7,2};

int T(int*a){
  // Loop over the ways to win
  for(c=0;c<16;c+=2)
    // Test for a win
    if(A(c)&A(c+1)&A(c+2))return A(c);
  return 0;
}

тестирование эшафот:

#include <stdlib.h>
#include <stdio.h>

int T(int*);

int main(int argc, char**argv){
  int input[9]={0};
  int i, j;
  for (i=1; i<argc; ++i){
    input[i-1] = atoi(argv[i]);
  };
  for (i=0;i<3;++i){
    printf("%1i  %1i  %1i\n",input[3*i+0],input[3*i+1],input[3*i+2]);
  };
  if (i = T(input)){
    printf("%c wins!\n",(i==1)?'X':'O');
  } else {
    printf("No winner.\n");
  }
  return 0;
}

наверное, можно было бы сделать лучше, но я не чувствую себя особенно умным прямо сейчас. Это просто чтобы убедиться Хаскелл получает представляет...

предполагая, что b уже существует, это поставит результат в w.

import List
a l=2*minimum l-maximum l
z=take 3$unfoldr(Just .splitAt 3)b
w=maximum:map a(z++transpose z++[map(b!!)[0,4,8],map(b!!)[2,4,6]])

предполагая ввод из stdin и вывод в stdout,

import List
a l=2*minimum l-maximum l
w b=maximum:map a(z++transpose z++[map(b!!)[0,4,8],map(b!!)[2,4,6]])where
 z=take 3$unfoldr(Just .splitAt 3)b
main=interact$show.w.read

В C#, 180 символов :

var s=new[]{0,0,0,1,2,2,3,6};
var t=new[]{1,3,4,3,2,3,1,1};
return(s.Select((p,i)=>new[]{g[p],g[p+t[i]],g[p+2*t[i]]}).FirstOrDefault(l=>l.Distinct().Count()==1)??new[]{0}).First();

(g будучи сетки)

вероятно, можно было бы улучшить... Я все еще работаю над этим ;)


Python, 140 символов

мой первый код golf, весом в здоровенный 140 символов (импорт заявление, я отказываю вам!):

import operator as o

def c(t):return({1:1,8:2}.get(reduce(o.mul,t[:3]),0))
def g(t):return max([c(t[x::y]) for x,y in zip((0,0,0,1,2,2,3,6),(1,3,4,3,3,2,1,1))])

чуть менее неясный g:

def g(t):return max([c(t[x::y]) for x,y in [[0,1],[0,3],[0,4],[1,3],[2,3],[2,2],[3,1],[6,1]]])

Решение C#.

умножьте значения в каждой строке, col & diagonal. Если result == 1, х побед. Если result == 8, o выигрывает.

int v(int[] b)
{
    var i = new[] { new[]{0,1,2}, new[]{3,4,5}, new[]{6,7,8}, new[]{0,3,6}, new[]{1,4,7}, new[]{2,5,8}, new[]{0,4,8}, new[]{2,4,6} };
    foreach(var a in i)
    {
        var n = b[a[0]] * b[a[1]] * b[a[2]];
        if(n==1) return 1;
        if(n==8) return 2;
    }
    return 0;
}

C#, 154 163 170 177 символы

заимствование нескольких методов из других представлений. (не знал, что C# позволяет вам вводить массивы)

static int V(int[] b)
{
   int[] a={0,1,3,1,6,1,0,3,1,3,2,3,0,4,2,2};
   int r=0,i=-2;
   while((i+=2)<16&&(r|=b[a[i]]&b[a[i]+a[i+1]]&b[a[i]+a[i+1]*2])==0){}
   return r;
}

C, 113 символов

f(int*b){char*s="012345678036147258048264";int r=0;while(!r&&*s){int q=r=3;while(q--)r&=b[*s++-'0'];}return r;}

Я думаю, что это работает? Мой первый код гольф, будь нежен.

каждые 3 цифры кодирует 3 ячейки, которые должны соответствовать. Внутреннее время проверяет триаду. Внешний пока проверяет все 8.