成为冠军


11

井字游戏拉丁!

这是一个真实的故事,因此名称已更改。

我的拉丁老师拉丁先生创建了自己的专有(无笑话)井字游戏变体。我们称其为tic-tac-latin。这款游戏很简单,基本上是井字游戏,四格四格。

正式规则声明

线可以是行,列或对角线。有两个符号,“ X”和“ O”,但是一个或两个都可以代替一个不同的符号。
当您的符号中有三个而另一个字符中的一个时,您得分为一。

这些安排得分:

---
-O--
XXXO
XOOX

O -XX
-  -
- X -
--- Ø

这些不得分:

----
XXXX
----
OOOO

----
XXX-
----
OOO-

只要一个玩家获得的积分多于另一个,就会赢得比赛。仅当棋盘被填满时,游戏才是平局。

挑战

解决这个游戏。您的工作是提供一种保证双赢的方法,以最佳结果为准。

您的解决方案可以选择从第一开始或从第二开始(因此可以选择它的符号)。在用户输入动作并相应显示更改的情况下,实现交互式游戏不是强制性的。它也可以是将输入作为游戏状态并输出新棋盘或其动作描述的功能或程序。每种选择每次移动都必须在大约十秒钟内运行。


与任何顺序的比赛对战时,玩家都必须获得最佳结果。这意味着您可以假设输入位置是您在播放器中可以到达的位置。提交的内容必须是确定性的,并不一定需要提供最佳的证明,但是,如果提交的内容(被殴打)被破译,您的提交将被视为无效(您可以将其保留,但可以在标题中添加(破译))。
这是一项艰巨的任务,因此任何有效的提交都是令人印象深刻的,值得我们接受,但是我将使代码高尔夫球成为主要的获胜标准。

通过从该列表中向下选择直到选择一名获胜者来选择获胜者。

  • 最短的解决方案实现,总能取胜
  • 最短的实施

1
“首先要考虑比赛的质量”您不认为这是主观的吗?
user48538

提供播放界面的任务似乎是编写完美播放器的外围。我建议简单地传递当前的游戏状态作为输入,并要求代码输出一个获胜的举动,或者甚至只是在完美玩法(赢,平局,输局)下进行评估。
xnor

1
通过进行低效率的蛮力搜索,解决方案可能会变得毫无特色。如果代码运行缓慢,您还可以吗?
xnor

1
如果您得分,您会赢得比赛,而在此过程中您不会为对手得分。 ”这是否意味着我只能在放置一个棋子时获胜,而在我的对手却不会赢球?如果此举为双方创造了获胜线,将会发生什么?
彼得·泰勒

1
@RohanJhunjhunwala您应该弄清允许输入的游戏状态,否则人们可能会利用当前未定义的输入格式,并选择一种对他们的解决方案有很大帮助的格式。
仅ASCII的

Answers:


6

Perl,147个字节(非竞争性,每次移动耗时超过10秒)

包括+4 -0p

程序播放X。它将扮演一个完美的游戏。

在STDIN上输入板,例如:

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

输出将是同一块板,所有X替换为O,反之亦然。空点将被一个数字填充,该数字指示如果X将在那里比赛的结果,则1意味着结果将是胜利,2平局和3失败。完成的游戏只是返回相同的位置,而颜色却相反。

在此示例中,输出为:

1O1X
1O33
O3O3
X33X

因此,X如果他在顶部和左侧的3个位置上都发挥作用,则该职位是赢家。其他所有动作都会失败。

如果您想知道移动后游戏如何继续,这种令人困惑的输出实际上很方便。由于该程序始终在播放,因此X您必须进行交换XO查看的移动O。例如,在这里很明显,X靠左上方的比赛获胜,但是如果X沿顶部的第三位比赛怎么办?只需复制输出,O替换您选择的举动,-然后再次替换所有其他数字,所以在这里:

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

导致:

3XXO
3X33
X3X3
O33O

显然,每一个举动O都应该输掉,那么如果他在左上角打球怎么输?再次这样做,方法是将O左上角的数字替换为-

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

给予:

XOOX
1O33
O3O3
X33X

因此,X只有一种赢得胜利的方法:

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

给予

OXXO
XX33
X3X3
O33O

情况O仍然没有希望。现在很容易看出,每一步都可以X立即获胜。让我们至少尝试连续3个O:

OXXO
XX--
X-X-
O-OO

给予:

XOOX
OO13
O3O3
X3XX

X参与唯一获胜的举动(注意,此举XXXO沿第三栏:

XOOX
OOO-
O-O-
X-XX

这里的输出是:

OXXO
XXX-
X-X-
O-OO

因为游戏已经结束了。您可以在第三栏看到获胜。

实际程序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)

将其应用于空板上,可以评估9506699的位置,这在我的计算机上需要30 Gb和41分钟。结果是:

2222
2222
2222
2222

因此,每一个开始的举动都会吸引。因此游戏是平局。

极端的内存使用情况主要是由使用递归引起的do$0。使用普通功能使用此154字节版本需要3Gb和11分钟:

#!/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

这是可以忍受的(但仍然太多了,某些东西仍然必须泄漏内存)。

结合多个加速因素,就可以得到以下160字节的版本(5028168个位置,空板4分钟和800M):

#!/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

最后一个0用于获胜(请勿与混淆O),1平局和2亏损。这一项的输出也更加令人困惑。在没有颜色交换的情况下,它为X填充了获胜的举动,但是如果输入游戏已经获胜,则仍然进行颜色交换并且不填充任何举动。

当然,随着电路板的装满,所有版本都会变得更快,并使用更少的内存。进行2或3次移动后,速度更快的版本将在10秒内产生一次移动。

原则上,此146字节版本也应起作用:

#!/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)

但是在我的机器上,它会触发perl错误并转储核心。

如果$$_||=删除by进行的6字节位置缓存,则所有版本原则上仍然可以使用,但是这会占用大量时间和内存,因此仅适用于几乎装满的电路板。但是从理论上讲,至少我有一个140字节的解决方案。

如果你把$\=(费用:3个字节)的前刚刚$@<=>0然后每个输出板之后,将整个板的状态:1X胜,0对于平局和-1损失。

这是基于上述最快版本的交互式驱动程序。驾驶员对游戏何时完成没有逻辑,因此您必须停下来。高尔夫规则知道。如果建议的举动返回且没有任何-替换,则游戏结束。

#!/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;
}

好答案。您能为我提供一些简单的方法来测试吗?理想情况下,希望看到一个交互式版本...(这是出于我的好奇心)。
Rohan Jhunjhunwala

@RohanJhunjhunwala好,添加了一个简单的交互式驱动程序
Ton Hospel

变量'$ move'未在prog.pl:2中声明
Rohan Jhunjhunwala

人类可以实施启发式解决方案吗?
Rohan Jhunjhunwala

@RohanJhunjhunwala刚刚重新检查了驱动程序。运行正常,$move在第11行上声明。我不知道是否有人为试探。该程序只是在游戏树上执行minimax,它没有任何战略知识。
Ton Hospel '16

2

JavaScript(ES6)392字节

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])

用法

“机器人”将扮演第二位。

绘制一个4x4的网格,编号如下:

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

让我们在浏览器控制台中运行它:放在f=代码之前

因此,如果我想从这里开始1,我会跑步f([1])([]),它将给我14

不错的举动...如果我2以后玩该怎么办?f([2,1])([14])。它将返回13

让步尝试投降。播放3f([3,2,1])([14,13])。喔0!你懂我!

0吗?f([0,2,1])([14,13])15好吧,让我们继续玩...

注意

  1. 互动玩。从开始f([your-step])([])

  2. 准备下一步。(请参见上面的演示)

  3. 帮助“机器人”输入其步骤。如果将其随机设置,则不会获得良好的效果。(就像f([1,2,4])([14,12])会给14-嘿,该机器人想继续进行13第二步!

简要总结

只要您不投降,该机器人就会扮演镜像动作。

感谢@EHTproductions告诉我我误读了游戏规则和高尔夫技巧:P

现在它还将检测是否有将死。如果是,请将其阻止!

它的优先级:块>镜像>(备用)寻找复制镜像的方法


我真的很喜欢“镜像移动”策略:)我可能会误会,但是这3,2,1对您和0机器人来说不是胜利吗?
ETHproductions 2016年

糟糕,我误解为“谁捕获了一种3种模式,另一种1种模式”。Lemme稍微调整了解决方案。.谢谢@ETHproductions。
Sunny Pun

几个打高尔夫球的技巧:[0,1,2,3].map(i=>{k(n=>n%4==i);k(n=>Math.floor(n/4)==i);})可以打高尔夫球[0,1,2,3].map(i=>k(n=>n%4==i)+k(n=>(n/4|0)==i))
ETHproductions 16/09/24

我认为这无法证明是可行的
Rohan Jhunjhunwala

0-14-12 -13破解
Rohan Jhunjhunwala
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.