Tic Tac Toe: cetak semua posisi yang mungkin tanpa duplikat

8

Tulis program yang menampilkan semua kemungkinan posisi Tic Tac Toe termasuk hasil permainan yang sesuai. Hindari output duplikat dari posisi yang sama.

Program tidak menerima input.

Aturan:

  • Output posisi harus terdiri dari 9 karakter, menggunakan Xdan Ountuk kotak yang diambil, dan karakter non-spasi putih sewenang-wenang untuk kotak kosong
  • Setiap posisi harus dicetak dalam 3 baris / kolom, dengan garis kosong sebagai pemisah antara dua posisi.
  • Ruang kosong / karakter garis / kotak kosong tambahan dipersilakan
  • Pemain X berjalan lebih dulu
  • The hasilnya dapat berupa:

    • X telah menang
    • O telah menang
    • Seri
    • Permainan sedang berlangsung

    Anda bebas memilih visualisasi hasil posisi yang sesuai, misalnya sebagai teks berwarna, atau sebagai anotasi teks, asalkan ditempatkan dekat posisi yang sesuai.

  • Posisi dianggap sama jika satu dapat diperoleh dari yang lain dengan rotasi atau mirroring. Posisi rangkap tidak boleh dicetak. (Dengan kata lain, cetak kelas kesetaraan saja.)
    Misalnya, cetak hanya satu dari yang berikut ini:
X••  ••X  •••  •••
•••  •••  •••  •••
•••  •••  X••  ••X
  • Ini , jadi kode terpendek menang!

Output sampel:

•••
•••
••• -

X••
•••
••• -

•X•
•••
••• -

•••
•X•
••• -


[…]


XXO
OOX
XXO /

OXO
XXX
OXO X

Petunjuk: Ada 765 posisi, dengan 91 kemenangan untuk X, 44 kemenangan untuk O, dan 3 seri.


Sebuah pertanyaan serupa telah diminta sebelumnya, tapi yang satu ini berbeda.

ThomasR
sumber
2
Selamat datang di PPCG! Setiap tantangan pada PPCG harus memiliki kriteria kemenangan. Apa kriteria untuk tantangan ini? Apakah ini kode-golf di mana program dengan bytecount terpendek menang?
user41805
Jika Anda mengkonfirmasi kriteria kemenangan, ini harus segera dibuka kembali. Saya belum menghitung posisi, tetapi saya bertanya-tanya, apakah papan kosong a) wajib b) dilarang c) opsional?
Level River St
Baru sadar, Anda sudah mengatakan kode terpendek menang. Mengedit dalam tag kode-golf. Kami biasanya mencetak dengan jumlah byte di sini, karena ada beberapa bahasa yang secara khusus diciptakan untuk menggunakan karakter multibyte untuk mempersingkat kode, yang tidak terlalu menarik.
Level River St
@StephenS Itu hal yang berbeda. Satu byte dapat memiliki nilai mulai 0-255. Situs web ini hanya dapat menampilkan karakter byte tunggal dalam kisaran 32-126 plus beberapa lainnya. Codepage 256 byte telah ada sejak lama dan saya sendiri telah memposting jawaban C di codepage 437. UTF-8 diciptakan karena codepage ini tidak memiliki karakter yang cukup untuk semua bahasa alami, tetapi UTF-8 tidak dirancang untuk menangani non-teks data dengan byte acak. Saya merujuk pada bahasa-bahasa seperti esolangs.org/wiki/Sclipting yang banyak menggunakan karakter Asia multibyte untuk mengeksploitasi celah penilaian karakter
Level River St
@LevelRiverSt ah, mengerti. Terima kasih atas informasinya :)
Stephen

Jawaban:

5

Jelly , 192 179 168 byte

5ĿḢṠµFf0L¬ị⁾-/µ5ĿḢṠµ?
U,µṚ,µ€µ“æɗþƑ’DịFs3,µ€€Fs9s3$€Ṣ
FSµ×’¬
ÑPµA_µọ9n1
,UŒDḢ$€;;ZS€f3,-3
Ç,SS€µṪµṪCµSṠ‘µ?÷Ḣ
Ça3Ŀa4Ŀ
3ṗ9_2s3$€ÇÐf2Ŀ€QḢ€µ;ÑFµ€µ3Ḷ’,“O¤X”yµ€Fs10µs3Gµ€j⁾¶¶

Cobalah online! (Butuh sekitar 30 detik, jadi bersabarlah).

Bagaimana itu bekerja

Ikhtisar tingkat tinggi:

Dalam langkah menengah, ini menyimpan X as 1, unplaced as 0, dan O as -1. Program ini menghasilkan semua 3 ^ 9 kemungkinan, kemudian hanya menyimpan posisi yang valid berdasarkan memenuhi tiga kriteria:

  • Ada 1 atau 0 lebih banyak X daripada O.
  • Tidak X dan O menang.
  • Jika X telah menang, ada 1 lebih banyak X daripada O. Jika O telah menang, ada jumlah Os dan X yang sama.

Kemudian, program ini mengganti setiap kondisi permainan dengan semua rotasi dan pantulannya untuk mendapatkan daftar semua kelas ekivalensi. Ini adalah operasi yang menghabiskan sebagian besar waktu.

Status permainan pertama diambil dari masing-masing kelas kesetaraan, lalu siapa yang menang dihitung.

Di mana ini terjadi Baris diberi nomor agar mudah dibaca

1: 5ĿḢṠµFf0L¬ị⁾-/µ5ĿḢṠµ?          ••calculates who wins (output as -1 or 1) or draw ("-") or in game ("/")
                 µ5ĿḢṠµ?          - if(someone won based on line 5):
   5ĿḢṠ                             - return 1 if X won and -1 if O won
       µ                          - else:
        Ff0L¬ị⁾-/                   - return "/" if the game is in progress and "-" if the game is at a tied end-state
2: U,µṚ,µ€µ“æɗþƑ’DịFs3,µ€€Fs9s3$€Ṣ••outputs the equivalence class to a game state
   U,                             - list of game state [reflected horizontally, unaltered]
     µṚ,µ€                        - for each, replace with the list [reflected vertically,unaltered]
          µ“æɗþƑ’DịFs3,µ€€        - for each, replace with the list [rotated 90 degrees counter-clockwise,unaltered]
                          Fs9s3$€ - reformat into list of game states
                                 Ṣ- Sort
3: FSµ×’¬                         ••outputs truthy iff there is the right number of `X`s to `O`s (condition 1)
   FS                             - d = number of `X`s minus number of `O`s
     µ×’                          - d*(d-1): falsey iff d is 0 or 1
        ¬                         - logical not, to return truthy iff d is 0 or 1
4: ÑPµA_µọ9n1                     ••outputs truthy iff there is the right number of winners (condition 2)
   Ñ                              - the winners. [-3,3] is what we want to return falsy on
    PµA_µ                         - 18 on [-3,3] and 0 otherwise
         ọ9n1                     - not divisible by 9 exactly once: 0 on [-3,3] and 1 otherwise
5: ,UŒDḢ$€;;ZS€f3,-3              ••outputs the number of times each player won.
   ,UŒDḢ$€;;Z                     - the diagonals, rows, and columns of a board
             S€                   - sum of each. Returns -3 or 3 iff the line is only 1s or -1s
               f3,-3              - filter out, keeping only 3s and -3s
6: Ç,SS€µṪµṪCµSṠ‘µ?÷Ḣ             ••return truthy iff the winner corresponds to the respective numbers of X and O (condition 3)
   Ç,SS€                          - list of winners and how many more Xs than Os there are
             µSṠ‘µ?               - if O won, then
        µṪ                          - how many more Xs than Os there are
                                  - else:
          µṪC                       - the complement of how many more Xs than Os there are
                   ÷Ḣ             - deal with no one winning
7: Ça3Ŀa4Ŀ                        ••return truthy iff the game state meets all three conditions
   Ç 3Ŀ 4Ŀ                        - the three conditions: on line 3,4, and 6
    a  a                          - joined by logical ANDs
8: 3ṗ9_2s3$€ÇÐf2Ŀ€QḢ€µ;ÑFµ€µ3Ḷ’,“O¤X”yµ€Fs10µs3Gµ€j⁾¶¶ ••do everything
   3ṗ9_2                        - all possible game states, with -1 for O and 1 for X
        s3$€                    - turn into two-dimensional game boards
            ÇÐf                 - filter based on line 6: if the board meets all three ocnditions
               2Ŀ€Q             - generate the equivalence classes for each and remove repeats based on line 1
                   Ḣ€           - get the first board from each class
                     µ;ÑFµ€     - append the winner to each board based on line 1
   µ3Ḷ’,“O¤X”yµ€                   - map each -1, 0, and 1 to the proper `O`,  `¤`, and `X`.
                Fs10µs3Gµ€         - format each board- winner combos
                          j⁾¶¶     - join the combos by double-newlines
fireflame241
sumber
2

Ruby, 305 byte

19683.times{|i|c=(i%3).to_s 
s=(9**8+i/3*6562).to_s(3)
w=0
t=(0..3).map{|j|r=s[1+j*2,9]
w|=1<<r[0..2].sum%8|1<<(c+r[j/2]+r[4+j/2]).sum%8
[r,r.reverse].min}.min
v=t[0..2]+$/+t[7]+c+t[3]+$/+t[6]+t[5]+t[4]
w&=65
b=v.sum%51
w<65&&b>w/64&&b<3-w%2&&t==s[1,9]&&puts(v.tr("012","X.O"),w<1?v.include?(?1):w%31,"")}

Ini bekerja sama dengan jawaban lain karena menghasilkan semua 3**9papan kemudian menyaring yang valid. Secara internal, kami menggunakan nomor terner 0=X 1=. 2=Odi mana dalam output. Iterasi cmelalui 3 nilai yang mungkin untuk pusat, dan smelalui 3**8 = 6561nilai untuk perimeter. Sebelum mengonversi i/3ke representasi string dari nomor terner, kami mengalikannya dengan 6562menduplikasi semua digit, dan menambahkan 3**16untuk memulai angka dengan 1, untuk memastikan ada nol di depan yang berlaku. wadalah kondisi menang - atur ini ke nol.

Untuk setiap papan Iterate melalui 4 rotasi digit suntuk menemukan versi terendah secara leksikal dari nomor terner 8 digit saat ini yang mewakili perimeter. Pada saat yang sama, tambahkan nilai ascii dari 3 digit pertama (baris teratas dari rotasi saat ini) dan gunakan ini untuk memeriksa kemenangan. Juga, tambahkan nilai ascii cdan sepasang angka yang berlawanan secara diametris untuk memeriksa apakah ada kemenangan melalui center.

Periksa apakah outputnya valid - Jika bit 1 dan 64 bit wditetapkan, kedua belah pihak menang - ini tidak valid. Periksa saldo X dan O (jika belum ada pemenang, itu bisa sama dengan X dan O atau satu lagi X - tetapi jika permainan dimenangkan, hanya ada satu nilai yang mungkin, karena pemenang harus yang terakhir.) Untuk menghindari menunjukkan rotasi yang berbeda dari papan yang sama, hanya keluaran jika versi perimeter terendah secara leksikal sesuai dengan nilai saat ini s[2,9].

Keluarkan papan , melestarikan simbol tr("012","X.O"). Status permainan ditampilkan di bawah papan. Jika w = 0, ini adalah truejika masih ada kotak kosong (permainan masih berlangsung) dan falsejika papan penuh. Jika wbukan nol kita mengeluarkan 1jika pemain 1 (X) telah menang atau 64%31==2jika pemain 2 (O) telah menang.

Tidak disatukan

19683.times{|i|                             #check 3**9 possibilities
  c=(i%3).to_s                              #centre square: 0=X, 1=. 2=O 
  s=(9**8+i/3*6562).to_s(3)                 #perimeter:multiply i/3 by 3**8+1 to duplicate digits, add 3**16 to give a 1 at left side to ensure leading zeros
  w=0                                       #set w=0 to clear wins (1´s bit holds win for X, 64´s bit holds win for O)
  t=(0..3).map{|j|                          #build an array of 4 different rotations of the perimeter
     r=s[1+j*2,9]                           #by taking different 9-character slices of s
     w|=1<<r[0..2].sum%8|                   #add ascii codes mod 8 for top row of current rotation, take sum modulo 8 to give sum of digits. set a bit in w (we only care about bits 0 and 6)
        1<<(c+r[j/2]+r[4+j/2]).sum%8        #do the same for one of the 4 lines through the centre. if j/2=0 check diagonal, if j/2=1 check horizontal/vertical. 
     [r,r.reverse].min}.min                 #add to the array the lexically lowest version(forward or reverse) of the current rotation. When the loop ends, find the lexically lowest version overall and assign to t.
  v=t[0..2]+$/+t[7]+c+t[3]+$/+t[6]+t[5]+t[4]#format the output into a square 012\n 7c3\n 654
  w&=65                                     #clear bits 1 through 5 of w, leave only bits 0 and 6 which are the ones of interest.
  b=v.sum%51                                #valid values of sum of ascii codes in output are 461 (equal 0's and 2's) and 460 (one more 0 than 2). Take modulo 51 to reduce these values to 1 and 2       
  w<65&&                                    #if a maximum of one player has a winning line (not both) and
  b>w/64&&                                  #b must be 1 or 2 (only 2 permitted if O's won)
  b<3-w%2&&                                 #b must be 1 or 2 (only 1 permitted if X's won)
  t==s[1,9]&&                               #s[2,9] is the lexically lowest version of the current board (to avoid duplicates) then 
  puts(v.tr("012","X.O"),                   #output the board, subsituting internal "012" characters for external "X.O"
  w<1?v.include?(?1):w%31,"")               #if nobody won, output true if game in still in play (1's present on board) else false
}                                           #if there is a winner, output (player) 1 for X, (player) w%31=2 for O. Also output a blank line.

Skema memeriksa

Diagram di bawah ini menunjukkan skema rotasi (dan win checking, dalam huruf kapital). Diagram ditampilkan tidak diputar. Empat rotasi berbeda diambil sebagai substring dari salinan ganda i/3, dengan 3 huruf kapital berurutan pada perimeter setiap diagram ("atas" per rotasi saat ini) menjadi 3 karakter pertama dalam substring 9 karakter. Untuk setiap rotasi, pembalikan 9-karakter (flip diagonal tentang AE atau sumbu CG) juga dicoba. Papan hanya keluaran jika nilai saat ini i/3adalah yang terendah secara leksikal dari semua rotasi dan cermin.

 ABC    abC    aBc    Abc 
 hZd    hZD    hZd    HZD
 gfE    GfE    GFE    Gfe
Level River St
sumber
2

Python 2 , 648 620 byte

import re
M=re.match
x='X';o='O'
R=lambda b,z=[6,3,0,7,4,1,8,5,2]:''.join(b[z[i]]for i in range(9))
p='XXX......|...XXX...|......XXX|X...X...X';q=p.replace(x,o)
def e(b):c=b.count;d=c(x)-c(o);w=M(p,b)or M(p,R(b));v=M(q,b)or M(q,R(b));return 0 if d not in[0,1]else 0 if w and v else(x if d==1 else 0)if w else(o if d==0 else 0)if v else'.'if'.'in b else'/'
h=set()
for n in range(3**9):
 b=reduce(lambda a,v:('.XO'[a[1]%3]+a[0],a[1]/3),range(9),('',n))[0]
 if b not in h:
	u=e(b)
	if u:print'%s\n%s\n%s %s\n'%(b[:3],b[3:6],b[6:],u)
	h.update(reduce(lambda a,v:a+[R(a[-2]),R(a[-1])],range(3),[b,R(b,[2,1,0,5,4,3,8,7,6])]))

Cobalah online!

Mungkin sedikit bermain golf kecil di sini dengan pendekatan ini; tapi tidak banyak.

Sunting: Thx to ovs, yang mencatat tweak mendapatkan 28 byte; dan 3 dari Artemis Fowl

Batalkan kode golf

Ide dasarnya di sini adalah: brute force masing-masing dari 3 ^ 9 = 19683 kemungkinan papan pengkodean. Melacak konjugat (rotasi dan refleksi) dari papan yang telah diperiksa sehingga Anda tidak menggandakan entri. Minimal, dewan yang valid harus memiliki jumlah X dan O yang sama atau lebih dari X daripada O. Tidak mungkin memiliki kemenangan untuk X dan kemenangan untuk O; ditambah beberapa kendala rewel tambahan.

import re
from collections import Counter

FLIP_XFRM = [2,1,0,5,4,3,8,7,6]
ROT_XFRM = [6,3,0,7,4,1,8,5,2]

def xfrm(b, xf):
    return ''.join(b[xf[i]] for i in range(9))

def flipIt(b):
    return xfrm(b,FLIP_XFRM)

def rotIt(b):
    return xfrm(b,ROT_XFRM)

def conjugates(b):
    conj = [b, flipIt(b)]
    for i in range(3):
        conj += [rotIt(conj[-2]), rotIt(conj[-1])]
    return conj

def tttToB(n):
    b = ''
    for i in range(9):
        b = '.XO'[n %3]+b
        n /= 3
    return b

def printBoard(b,outcome='.'):
    print '%s\n%s\n%s %s\n' % (b[:3],b[3:6],b[6:],outcome)

def evalBoard(b):
    c = Counter(b)
    if c['X']-c['O'] not in [0,1]:
        return False

    p1 = 'XXX......|...XXX...|......XXX|X...X...X'
    p2 = p1.replace('X','O')

    br = rotIt(b)
    w1 = re.match(p1,b) or re.match(p1,br)    
    w2 = re.match(p2,b) or re.match(p2,br)    
    if w1 and w2:
        return False

    if w1:
        return 'X' if c['X']==c['O']+1 else False

    if w2:
        return 'O' if c['X']==c['O'] else False

    if '.' in b:
        return '.'
    else:
        return '/'

def main():
    history = set()
    for m in range(3**9):
        b = tttToB(m)
        if b not in history:
            outcome = evalBoard(b)
            if outcome:
                printBoard(b,outcome)
            history.update(conjugates(b))

main()
Chas Brown
sumber
Saya tidak berpikir kamu membutuhkan Counter. Anda dapat menggantinya denganc=b.count;d=c(x)-c(o)
ovs
@ovs: sangat diperhatikan!
Chas Brown
Besar yang panjang returnbisareturn 0if d not in[0,1]else 0if w and v else(x*(d==1))if w else(o*(d==0))if v else'.'if'.'in b else'/'
Zachary
Posting lama saya tahu, tetapi ini tampaknya 623 byte? Anda dapat mengganti indentasi tingkat kedua (2 spasi) dengan satu tab untuk 620 byte, mungkin Anda bermaksud melakukan ini?
Artemis masih tidak percaya
@ ArtemisFowl Yup, ini sebelum saya menemukan TIO - mudah membuat kesalahan penghitungan!
Chas Brown