Baue eine perfekte KI für das Spiel 15


8

In Spiel 15 wählen zwei Spieler abwechselnd Zahlen von 1 bis 9 aus (ohne eine Zahl auszuwählen, die einer der Spieler bereits ausgewählt hat). Ein Spieler gewinnt, wenn er drei Zahlen hat, die sich zu 15 addieren. Wenn alle Zahlen ausgewählt wurden und keine Kombination der beiden Zahlen 15 ergibt, ist das Spiel ein Unentschieden.

Ihre Aufgabe ist es, eine Funktion zu erstellen, die den Status eines 15er-Spiels annimmt (in einer beliebigen Form dargestellt) und die Zahl zurückgibt, die als nächstes bewegt werden soll. Diese fungiert als KI, um das Spiel mit einem anderen Spieler zu spielen. Sie können davon ausgehen, dass die Position legal ist (kein Spieler hat mehr als eine Zahl mehr als der andere Spieler, und kein Spieler hat bereits drei Zahlen, die sich zu 15 addieren).

Die KI muss perfekt sein - das heißt, wenn sie eine Gewinnposition erhält, muss sie einen Gewinn erzwingen, und wenn sie eine Position ohne Verlust erhält (eine Position, in der ihr Gegner keine Gewinnstrategie hat), darf sie ihre nicht zulassen Gegner, um ihm eine verlorene Position zu geben (was möglich ist, da 15 ein gelöstes Spiel ist).

Der kürzeste Code gewinnt.

(Hinweis: Ich akzeptiere die derzeit kürzeste Antwort und ändere sie, wenn eine kürzere Antwort angezeigt wird.)


Verstehe ich richtig, dass wir verlieren dürfen, wenn wir in eine gewinnbare Position geraten, in die unsere KI nicht hätte gelangen können?
John Dvorak

Ja, das ist richtig. Wenn der KI ein Spielstatus angezeigt wird, den sie nicht gewinnen oder unentschieden spielen kann (z. B. eine Doppelfalle), darf sie verlieren. Die KI sollte jedoch niemals zulassen, dass das Spiel von einem leeren Brett in den verlorenen Zustand gerät.
Joe Z.

gehe ich richtig davon aus, dass die KI immer die erste ist, die spielt?
John Dvorak

1
hmm ... es scheint, wir dürfen unentschieden spielen, auch wenn wir einen Sieg erzwingen können. Ist das wahr?
John Dvorak

2
Hah. Ich habe gerade von diesem Spiel erfahren und als ich nach Hause kam, musste ich es hier posten. Leider bin ich geschlagen worden.
Standby

Antworten:


6

GolfScript ( 129 86 81 85 75 Zeichen)

{:C[{.`{1$-\{+15\-}+%}:T+/}/10,1>C~+-:^{.{^T&}+C/,2<*T}/5^{1&}$][]*^&0=}:N;

Erwartetes Eingabeformat: [[int int ...][int int ...]]Dabei ist die erste Liste meine Nummer und die zweite Liste die Nummer meines Gegners. Fügen Sie für interaktive Tests ~Nam Ende des Skripts eine Zeichenfolge in diesem Format hinzu: z

$ golfscript.rb 15.gs <<<"[[5][2 8]]"
9
$ golfscript.rb 15.gs <<<"[[2][5 8]]"
6

Heuristik:

  1. Wenn ich diesen Zug gewinnen kann, mach es
  2. Wenn der Gegner in der nächsten Runde gewinnen würde, blocken Sie
  3. Wenn ich den Gegner auf ein Feld zwingen kann, das ihn daran hindert, eine Gabel zu erstellen, dann tue es
  4. 5 ist die einzige Zahl, die auf vier Arten zum Gewinnen beitragen kann. Schnappen Sie sie sich, falls verfügbar.
  5. Bevorzugung von Gewinnchancen

Testrahmen:

{:Set;[1 5 9 1 6 8 2 4 9 2 5 8 2 6 7 3 4 8 3 5 7 4 5 6]3/{.Set&=},!!}:isWin;
{
    # Mine His
    .isWin{
        "Lost "@`@`++puts
    }{
        # If there are available moves, it's my move.
        # If I won before my move, I've still won after it.
        1$1$+,9<!!{
            # my move
            1$1$[\\]N 2$\+
            # Mine His Mine'
            .isWin!{
                # his move
                10,1>2$2$+-{
                    2$\+1$\
                    # Mine His Mine' Mine' His'
                    fullTest
                    # Mine His Mine'
                }/
            }*;
        }*;;
    }if
}:fullTest;
# I move first
'[][]fullTest'puts [][]fullTest
# He moves first
'[][1]fullTest'puts [][1]fullTest
'[][2]fullTest'puts [][2]fullTest
'[][5]fullTest'puts [][5]fullTest

Können Sie diese Eingabe für mich durchlaufen: [5][3](sollte entweder 4 oder 8 zurückgeben).
Joe Z.

9- aber dann scheinen Sie die Regeln geändert zu haben.
Peter Taylor

Auch warum nur 4oder 8?
Peter Taylor

Oh warte, egal, ich habe mich geirrt. Alles außer 7sollte funktionieren, 9ist also akzeptabel.
Joe Z.

Außerdem wollte ich die Regeln nicht ändern. Es ist nur so, dass ich sie beim ersten Mal falsch formuliert hatte.
Joe Z.

4

Ruby, 330 315 341 Zeichen

def m i
$_=i.join
l='159258357456168249267348'.scan /(.)(.)(.)/
return 1 if /^5(28|46|64|82)$/
return 4 if /^[258]{3}$/
return 2 if /^[456]{3}$/
i.each{|i|l.map{|l|return l if(l-=i).size==1&&!/[#{l=l[0]}]/}}
.map{|i|l.map{|m|l.map{|l|return (l&m)[0] if(z=l-i|m-i).size==3&&l!=m&&!/[#{z.join}]/}}}
"524681379".chars{|z|return z if !/#{z}/}
end

Ich werde die Details vorerst zurückhalten, aber sagen wir, dass es auf dem optimalen Algorithmus für ein ähnliches Problem basiert, das ebenfalls gelöst wurde und dessen optimaler Algorithmus hier genauso gut funktioniert hat. Es wurden Annahmen getroffen - dies wählt schlechte Züge in Situationen aus, die von diesem Algorithmus nicht erzeugt werden können, wenn er gegen einen anderen Spieler spielt, sondern nur von zwei Spielern gegeneinander.

Eingabe: Ein Array aus zwei Arrays mit einstelligen Zeichenfolgen. Jedes Array repräsentiert die Werte eines Spielers - der erste ist die KI, der zweite ist der Gegner.

Ausgabe: entweder eine Zahl oder eine einstellige Zeichenfolge. Sie sind semantisch äquivalent. Die Normalisierung auf Zeichenfolgen würde 8 Zeichen kosten.

Drei weitere Zeichen können gespeichert werden, wenn wir die vom Anrufer angegebene Reihenfolge der Zahlen annehmen. Ändern Sie den regulären Ausdruck in L5 in /^285$/oder /^258$/abhängig von der Reihenfolge, die aus dem Spiel hervorgeht (opponent)5-(ai)2-(opponent)8.


Sieht so aus, als hätten Sie in den letzten drei Zeilen ein einfaches Speichern, indem Sie einfach 5zum Anfang Ihrer Präferenzreihenfolge wechseln.
Peter Taylor

@ah, ja, danke. Ich hatte einen weiteren Schritt dazwischen, den ich entfernt habe (Spezialgehäuse nach oben) und ich habe vergessen, die umgebenden Schritte zusammenzuführen. Ich werde bearbeiten, wenn ich nach Hause komme (es sei denn, Sie melden sich freiwillig dazu).
John Dvorak

1

GolfScript ( 90 85 84 Zeichen)

{.~.,3/*..2%.@^++3*3/{~++15=},,*10,1>2$~+-@`{([2$+]+v[0=~)\]}+%[1,]or$0=+}:v{1=}+:N;

Dies verfolgt einen völlig anderen Ansatz, ist jedoch möglicherweise anfällig für Optimierungen, um die heuristische zu übertreffen. Hier führen wir eine vollständige Spielbaumanalyse durch, unglaublich langsam. (Nein, ich meine es ernst. Es dauert mehrere Stunden , um den vollständigen Test auszuführen, hauptsächlich aufgrund dessen, `{...}+was den aktuellen Status zur Schleife für den nächsten Zug hinzufügt.) Beachten Sie, dass der schwierige Teil darin besteht, einen Gewinnstatus zu identifizieren (derzeit ein Drittel des Codes).

Es gibt einige hässliche Hacks in den nicht rekursiven Abschnitten. Insbesondere wenn die Position als verlierend identifiziert wird, nehmen wir unsere Positionen als [value move]Array, wobei wir uns darauf verlassen, dass die Bewegung irrelevant ist und der Wert nicht Null ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.