Trở thành nhà vô địch


11

Tic-Tac-Latin!

Đây là một câu chuyện có thật, vì vậy tên đã được thay đổi.

Giáo viên Latin của tôi, ông Latin, đã tạo ra biến thể ngón chân tic tac độc quyền (không đùa) của riêng mình. Hãy gọi nó là tic-tac-latin. Trò chơi rất đơn giản, về cơ bản là tic tac toe được chơi trên một lưới bốn nhân bốn.

Tuyên bố quy tắc chính thức

Một dòng là một hàng, cột hoặc một đường chéo. Có hai biểu tượng, 'X' và 'O', nhưng một hoặc cả hai có thể được thay thế cho một biểu tượng khác.
Bạn ghi được một điểm khi bạn có ba biểu tượng của mình và một trong những nhân vật khác.

Những điểm sắp xếp này:

--- Ô
-O--
XXXO
XOOX

Ô -XX
- Ô -
- X -
--- Ô

Những điểm này không được:

----
XXXX
----
OOo

----
XXX-
----
OOo-

Trò chơi được chiến thắng bất cứ khi nào một người chơi có nhiều điểm hơn người khác. Trò chơi chỉ là một trận hòa nếu bảng được lấp đầy.

Thử thách

Giải quyết trò chơi này. Công việc của bạn là cung cấp một cách để đảm bảo một chiến thắng hoặc hòa, bất cứ điều gì là kết quả tối ưu.

Giải pháp của bạn có thể chọn bắt đầu trước hoặc thứ hai (và do đó có thể chọn biểu tượng đó). Không bắt buộc phải thực hiện một trò chơi tương tác nơi người dùng nhập liệu di chuyển và màn hình tương ứng thay đổi. Nó cũng có thể là một chức năng hoặc chương trình lấy đầu vào làm trạng thái trò chơi và đưa ra một bảng mới hoặc mô tả di chuyển của chúng . Hoặc là tùy chọn phải chạy trong khoảng mười giây cho mỗi lần di chuyển được thực hiện.


Chơi cầu thủ của bạn chống lại bất kỳ chuỗi di chuyển phải cho kết quả tối ưu. Điều này có nghĩa là bạn có thể giả định vị trí đầu vào là vị trí có thể tiếp cận khi chơi với người chơi của bạn. Đệ trình phải có tính xác định và không nhất thiết phải cung cấp bằng chứng về sự tối ưu, nhưng nếu chúng bị bẻ khóa (do bị đánh), bài nộp của bạn sẽ bị coi là không hợp lệ (bạn có thể bỏ nó, nhưng thêm (bẻ khóa) vào tiêu đề).
Đây là một nhiệm vụ không hề nhỏ, vì vậy bất kỳ bài nộp hợp lệ nào cũng rất ấn tượng và xứng đáng với một đánh dấu được chấp nhận, nhưng tôi sẽ biến golf thành tiêu chí chiến thắng chính.

Người chiến thắng được chọn bằng cách đi xuống danh sách này cho đến khi một người chiến thắng được chọn.

  • Giải quyết ngắn nhất mà luôn luôn chiến thắng
  • Thực hiện ngắn nhất

1
"Đầu tiên, chất lượng chơi được nhìn vào" Bạn có nghĩ rằng nó chủ quan không?
dùng48538

Nhiệm vụ cung cấp một giao diện để chơi dường như ngoại vi của việc viết một trình phát hoàn hảo. Tôi khuyên bạn chỉ nên chuyển trạng thái trò chơi hiện tại làm đầu vào và yêu cầu mã để tạo ra một nước đi chiến thắng, hoặc thậm chí đơn giản là một đánh giá theo cách chơi hoàn hảo (thắng, hòa, thua).
xnor

1
Một giải pháp có thể được đánh gôn bằng cách thực hiện tìm kiếm vũ lực không hiệu quả. Bạn có ổn không nếu mã chạy rất chậm?
xnor

1
" Bạn thắng trò chơi nếu bạn ghi bàn và trong quá trình không ghi bàn cho đối thủ của mình. " Điều này có nghĩa là tôi chỉ có thể giành chiến thắng khi đặt một quân cờ chứ không phải khi đối thủ của tôi làm vậy? Điều gì xảy ra nếu một nước đi tạo ra các dòng chiến thắng cho cả hai người chơi: trò chơi được vẽ hoặc chơi trên?
Peter Taylor

1
@RohanJhunjhunwala Bạn nên làm rõ đầu vào được phép của trạng thái trò chơi, nếu không, mọi người có thể tận dụng định dạng đầu vào hiện không xác định và chọn định dạng giúp giải pháp của họ rất nhiều.
ASCII - chỉ

Câu trả lời:


6

Perl, 147 byte (không cạnh tranh, mất hơn 10 giây mỗi lần di chuyển)

Bao gồm +4 cho -0p

Chương trình chơi X. Nó sẽ chơi một trò chơi hoàn hảo.

Nhập bảng trên STDIN, ví dụ:

tictaclatin.pl
-X-O
-X--
X-X-
O--O
^D

Ouptut sẽ là cùng một bảng với tất cả được Xthay thế bởi Ovà ngược lại. Các điểm trống sẽ được lấp đầy bằng một con số cho biết kết quả nếu X chơi ở đó, với 1nghĩa là kết quả sẽ là thắng, 2hòa và 3thua. Một trò chơi đã hoàn thành chỉ trả về cùng một vị trí với màu sắc đảo ngược.

Trong ví dụ này, đầu ra sẽ là:

1O1X
1O33
O3O3
X33X

Vì vậy, vị trí này là một chiến thắng cho Xnếu anh ta chơi ở 3 vị trí dọc theo đỉnh và bên trái. Tất cả các động thái khác bị mất.

Đầu ra khó hiểu này thực sự tiện lợi nếu bạn muốn biết trò chơi tiếp tục như thế nào sau khi di chuyển. Vì chương trình luôn phát Xnên bạn phải trao đổi XOđể xem các động thái cho O. Ví dụ ở đây khá rõ ràng là Xchiến thắng bằng cách chơi ở phía trên bên trái, nhưng nếu Xchơi ở vị trí thứ ba dọc theo đỉnh thì sao? Chỉ cần sao chép đầu ra, đặt một Ovị trí di chuyển bạn chọn và thay thế tất cả các số khác -một lần nữa, vì vậy ở đây:

-OOX
-O--
O-O-
X--X

Kết quả là:

3XXO
3X33
X3X3
O33O

Rõ ràng mỗi lần di chuyển đều Ophải thua, vậy làm thế nào để anh ta thua nếu chơi ở phía trên bên trái? Một lần nữa làm điều này bằng cách đặt Oở trên cùng bên trái và thay thế các chữ số bằng cách -:

OXXO
-X--
X-X-
O--O

Tặng:

XOOX
1O33
O3O3
X33X

Vì vậy, X chỉ có một cách để giành chiến thắng:

XOOX
OO--
O-O-
X--X

Bố thí

OXXO
XX33
X3X3
O33O

Tình hình Ovẫn vô vọng. Bây giờ thật dễ dàng để thấy rằng mọi di chuyển đều cho phép Xgiành chiến thắng ngay lập tức. Ít nhất hãy cố gắng đi trong 3 giờ liên tiếp:

OXXO
XX--
X-X-
O-OO

Tặng:

XOOX
OO13
O3O3
X3XX

Xđóng vai trò chiến thắng duy nhất (lưu ý rằng điều này thực hiện XXXOdọc theo cột thứ ba:

XOOX
OOO-
O-O-
X-XX

Ở đây đầu ra là:

OXXO
XXX-
X-X-
O-OO

bởi vì trò chơi đã kết thúc Bạn có thể thấy chiến thắng trên cột thứ ba.

Chương trình thực tế tictaclatin.pl:

#!/usr/bin/perl -0p
y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/sx;$@<=>0||s%-%$_="$`O$'";$$_||=2+do$0%eg&&(/1/||/2/-1)

Áp dụng cho bảng trống, điều này đánh giá các vị trí 9506699, mất 30Gb và 41 phút trên máy tính của tôi. Kết quả là:

2222
2222
2222
2222

Vì vậy, mọi di chuyển bắt đầu rút ra. Vì vậy, trò chơi là một trận hòa.

Việc sử dụng bộ nhớ cực đoan chủ yếu là do đệ quy sử dụng do$0. Sử dụng phiên bản 154 byte này bằng chức năng đơn giản cần 3Gb và 11 phút:

#!/usr/bin/perl -0p
sub f{y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/sx;$@<=>0||s%-%$_="$`O$'";$$_||=2+&f%eeg&&(/1/||/2/-1)}f

cái nào dễ chịu hơn (nhưng vẫn còn quá nhiều, thứ gì đó vẫn phải bị rò rỉ bộ nhớ).

Kết hợp một số tốc độ tăng tốc dẫn đến phiên bản 160 byte này (5028168 vị trí, 4 phút và 800M cho bảng trống):

#!/usr/bin/perl -0p
sub f{y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/osx;$@<=>0||s%-%$_="$`O$'";$a{$_}//=&f+1or return 1%eeg&&/1/-1}f

Cái cuối cùng đó sử dụng 0cho một chiến thắng (đừng nhầm lẫn với O), 1cho một trận hòa và 2thua. Đầu ra của cái này cũng khó hiểu hơn. Nó lấp đầy trong nước cờ chiến thắng cho X trong trường hợp thắng mà không đổi màu, nhưng nếu trò chơi đầu vào đã thắng thì vẫn là đổi màu và không điền vào bất kỳ động thái nào.

Tất cả các phiên bản tất nhiên sẽ nhanh hơn và sử dụng ít bộ nhớ hơn khi bảng đầy lên. Các phiên bản nhanh hơn sẽ tạo ra một động thái trong vòng dưới 10 giây ngay sau khi 2 hoặc 3 động tác được thực hiện.

Về nguyên tắc phiên bản 146 byte này cũng sẽ hoạt động:

#!/usr/bin/perl -0p
y/XO/OX/,$@=-$@while/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^/sx,--$|;$@<=>0||s%-%$_="$`O$'";$$_||=2+do$0%eg&&(/1/||/2/-1)

nhưng trên máy của tôi, nó kích hoạt lỗi perl và bỏ lõi.

Về nguyên tắc, tất cả các phiên bản sẽ vẫn hoạt động nếu bộ nhớ đệm vị trí 6 byte được thực hiện bằng cách $$_||=loại bỏ nhưng điều đó sử dụng rất nhiều thời gian và bộ nhớ mà nó chỉ hoạt động cho các bảng gần như đầy. Nhưng trên lý thuyết ít nhất tôi có một giải pháp 140 byte.

Nếu bạn đặt $\=(chi phí: 3 byte) ngay trước $@<=>0thì mỗi bảng đầu ra sẽ được theo sau bởi trạng thái của toàn bộ bảng: 1cho Xthắng, 0cho hòa và -1thua.

Dưới đây là trình điều khiển tương tác dựa trên phiên bản nhanh nhất được đề cập ở trên. Trình điều khiển không có logic khi trò chơi kết thúc nên bạn phải tự dừng lại. Các mã golf biết mặc dù. Nếu di chuyển được đề xuất trở lại mà không bị -thay thế bởi bất cứ điều gì, trò chơi kết thúc.

#!/usr/bin/perl
sub f{
    if ($p++ % 100000 == 0) {
        local $| = 1;
        print ".";
    }
y/XO/OX/,$@=-$@while$|-=/(@{[map{(O.".{$_}O"x3)=~s%O%Z|$`X$'|Z%gr}0,3..5]})(?{$@++})^|$/osx;$@<=>0||s%-%$_="$`O$'";$a{$_}//=&f+1or return 1%eeg&&/1/-1}

# Driver
my $tomove = "X";
my $move = 0;
@board = ("----\n") x 4;
while (1) {
    print "Current board after move $move ($tomove to move):\n  ABCD\n";
    for my $i (1..4) {
        print "$i $board[$i-1]";
    }
    print "Enter a move like B4, PASS (not a valid move, just for setup) or just press enter to let the program make suggestions\n";
    my $input = <> // exit;
    if ($input eq "\n") {
        $_ = join "", @board;
        tr/OX/XO/ if $tomove eq "O";
        $p = 0;
        $@="";
        %a = ();
        my $start = time();
        my $result = f;
        if ($result == 1) {
            tr/OX/XO/ if $tomove eq "O";
            tr/012/-/;
        } else {
            tr/OX/XO/ if $tomove eq "X";
            tr/012/123/;
        }
        $result = -$result if $tomove eq "O";
        my $period = time() - $start;
        print "\nSuggested moves (evaluated $p positions in $period seconds, predicted result for X: $result):\n$_";
        redo;
    } elsif ($input =~ /^pass$/i) {
        # Do nothing
    } elsif (my ($x, $y) = $input =~ /^([A-D])([1-4])$/) {
        $x = ord($x) - ord("A");
        --$y;
        my $ch = substr($board[$y],$x, 1);
        if ($ch ne "-") {
            print "Position already has $ch. Try again\n";
            redo;
        }
        substr($board[$y],$x, 1) = $tomove;
    } else {
        print "Cannot parse move. Try again\n";
        redo;
    }
    $tomove =~ tr/OX/XO/;
    ++$move;
}

Câu trả lời của NIce. Bạn có thể cung cấp một số cách dễ dàng để tôi kiểm tra điều này? Lý tưởng nhất là nó rất thích xem một phiên bản tương tác ... (đây là sự tò mò cho sự tò mò của riêng tôi).
Rohan Jhunjhunwala

@RohanJhunjhunwala Ok, đã thêm một trình điều khiển tương tác đơn giản
TonMedel

Biến '$ move' không được khai báo tại prog.pl:2
Rohan Jhunjhunwala

Có một giải pháp heuristic mà một con người có thể thực hiện?
Rohan Jhunjhunwala

@RohanJhunjhunwala Chỉ cần kiểm tra lại chương trình điều khiển. Chạy tốt, $moveđược khai báo trên dòng 11. Tôi không biết nếu có một heuristic con người. Chương trình này chỉ thực hiện minimax trên cây trò chơi, nó không có bất kỳ kiến thức chiến lược nào .
TonMedel

2

JavaScript (ES6) 392 byte

a=>b=>(c="0ed3b56879a4c21f",r=[],k=f=>r.push([a.filter(f),b.filter(f)]),[0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i)),k(n=>n%5==0),k(n=>n&&n-15&&!(n%3)),g=r.find(o=>(o[0].length==1&&o[1].length==2)||(o[0].length==2&&o[1].length==1)),g?parseInt(c[30-[...g[0],...g[1]].map(i=>parseInt(c[i],16)).reduce((p,c)=>p+c)],16):[...a,...b].indexOf(15-a[0])+1?15-a.find(i=>b.indexOf(15-i)==-1):15-a[0])

Sử dụng

"Bot" sẽ chơi thứ hai.

Vẽ một lưới 4 x 4 được đánh số như thế này:

+----+----+----+----+
|  0 |  1 |  2 |  3 |
+----+----+----+----+
|  4 |  5 |  6 |  7 |
+----+----+----+----+
|  8 |  9 | 10 | 11 |
+----+----+----+----+
| 12 | 13 | 14 | 15 |
+----+----+----+----+

Hãy chạy cái này trong bảng điều khiển trình duyệt: Chỉ cần đặt f=trước mã

Vì vậy, nếu tôi muốn bắt đầu 1, tôi sẽ chạy f([1])([])và nó sẽ cho tôi 14.

Di chuyển tốt đẹp ... Nếu tôi chơi 2sau đó thì sao? f([2,1])([14]). Nó sẽ trở lại 13.

Hãy thử đầu hàng. Chơi 3. f([3,2,1])([14,13]). Ôi 0! Bạn đã cho tôi!

Chơi 0? f([0,2,1])([14,13]). 15Ok, hãy tiếp tục chơi ...

Ghi chú

  1. Chơi tương tác. Bắt đầu với f([your-step])([]).

  2. Chuẩn bị bước tiếp theo của bạn. (Xem bản demo ở trên)

  3. Giúp "bot" nhập các bước của nó. Nó sẽ không cho bạn kết quả tốt nếu bạn đặt cho nó một cài đặt ngẫu nhiên. (Giống như f([1,2,4])([14,12])sẽ cho 14- Hey bot muốn chơi trên 13bước thứ hai của nó!

Bản tóm tắt ngắn gọn

Miễn là bạn không đầu hàng, bot sẽ chơi trò di chuyển gương.

Cảm ơn @EHTproductions đã nói với tôi rằng tôi đã đọc sai luật chơi và mẹo chơi gôn: P

Bây giờ nó cũng sẽ phát hiện nếu nó có checkmate. Nếu có, chặn nó!

Ưu tiên của nó: Chặn> Gương> (dự phòng) tìm cách tái tạo gương


Tôi thực sự thích chiến thuật "di chuyển gương" :) Tôi có thể hiểu nhầm, nhưng không phải 3,2,1vì bạn và 0bot là chiến thắng cho bạn?
Sản xuất ETH

Rất tiếc, tôi đã hiểu nhầm là "người nắm bắt mô hình 3 loại và 1 loại khác". Hãy chỉnh sửa giải pháp một chút .. Cảm ơn @ETHproductions.
Nắng Pun

Một vài mẹo chơi gôn: [0,1,2,3].map(i=>{k(n=>n%4==i);k(n=>Math.floor(n/4)==i);})có thể chơi gôn [0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i)).
Sản xuất ETH

Tôi không nghĩ rằng điều này có thể chiến thắng được
Rohan Jhunjhunwala

0 - 14 - 12 -13 làm nứt nó
Rohan Jhunjhunwala
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.