Algorithmus zum Spiel "PrimeGame"

  • Hallo,

    ich versuche gerade ein Spiel in AutoIt zu Implementieren und überleg mir gerade verschiedene Spieler-typen und deren Strategien.

    Zum Spiel:


    Gegeben ist die Menge der natürlichen Zahlen zwischen 1 und einer beliebigen
    Obergrenze. Zwei Spieler wählen abwechselnd eine Zahl aus dieser Menge, so
    lange bis keine Zahlen mehr vorhanden sind. Bei jedem Zug erhält der
    betreffende Spieler den Zahlenwert der ausgewählten Zahl gut geschrieben, sein
    Gegner erhält die Zahlenwerte aller noch vorhandenen Teiler der ausgewählten
    Zahl; danach werden die ausgewählte Zahl und alle ihre Teiler entfernt und der
    andere Spieler ist am Zug.

    Spieler 1: Zieht nur Primzahlen.

    Spieler 2: Zieht Primzahlen wenn vorhanden oder kleinste Zahl.

    Spieler 3: Zieht Random Zahlen

    Spieler 4: Rechnet die besten 10 Züge im Vorraus aus.


    Habt ihr eine Idee wie man einen Spieler entwickeln kann der immer gewinnt ?

    Es geht mir nur um die Logik nicht um die Implementierung in AutoIt.


    Vielen Danke im voraus und bedanke mich für jeden Beitrag  ;)

  • Hi,
    ich versuche mich mal grob an einer Lösung:

    Spoiler anzeigen


    Vielleicht gibt dir der Pseudocode, sofern er überhaupt lesbar und nachvollziehbar ist, ein paar Anregungen :)

    Wer immer nur das tut, was er bereits kann - wird auch immer nur das bleiben, was er bereits ist!

  • Spoiler anzeigen

    Also...
    Ich würde meine unbesiegbare KI einfach die größte Zahl aus der Zahlenmenge ziehen lassen welche keinen Teiler besitzt. So würde der Gegner nie Punkte bei einem Zug der KI bekommen. So ist es schon unmöglich zu gewinnen, weil die größten Zahlen einfach von der KI weggeschnappt werden. Und jede andere Zahl die dann der Spieler zieht bekommt dann meine KI.

    Ich habe hier auch eine Spielrunde mit den Zahlen 1 bis 20 niedergeschrieben. Der Spieler zieht immer eine zufällige Zahl (habe ich mit Random in AutoIt gemacht) und die KI wie oben beschrieben:



    Ich glaube ich brauche das hier nicht weiter ausführen. Wie man sieht hat meine KI die meisten Punkte durch den Spieler erhalten und sich die höchst Mögliche Zahl geschnappt, ohne den Gegner auch nur einen Punkt zu überlassen.

    Ich möchte meine Idee jetzt nicht mathematisch belegen. Wäre mir zu viel Aufwand das ganze tatsächlich zu berechnen...

    €dit:
    Mir ist gerade ein noch viel besserer Algorithmus eingefallen. Man berechnet den tatsächlichen Punktetechnischen Vorteil den eine Zahl einbringt. Dafür subtrahiert man alle noch vorhandenen Teiler der Zahlenmenge durch die gewählte Zahl. Das Endergebnis liefert einen tatsächlichen Punktevorsprung. Die Zahl die den größten Punktevorsprung bringt, ist die Zahl welche der Algorithmus wählt.

    Nehmen wir einmal an folgende Zahlen wären noch in der Zahlenmenge: 3, 5, 7, 12, 14

    Nun wird zuerst der Punktevorsprung für die Zahl 14 gesucht. Der einzige Teiler wäre die Zahl 7. Also ist der Punktevorsprung 14 - 7 Punkte. Hier eine Liste für alle 5 Zahlen:

    Code
    Zahl 14:   14 - 7 = 7
    Zahl 12:   12 - 3 = 9
    Zahl 7:     7 - 0 = 7
    Zahl 5:     5 - 0 = 5
    Zahl 3:     3 - 0 = 3

    Die Zahl 12 hat den größten Punktevorsprung. Also ist dies die Zahl welche am meisten Sinn macht zu wählen. Ich hoffe es ist so verständlich. :P


    Ich hoffe ich konnte dir damit weiter helfen! LG. Make ^^

    2 Mal editiert, zuletzt von Yjuq (31. Mai 2013 um 17:21)

  • Hi

    Zitat

    sein
    Gegner erhält die Zahlenwerte aller noch vorhandenen Teiler der ausgewählten
    Zahl; danach werden die ausgewählte Zahl und alle ihre Teiler entfernt und der
    andere Spieler ist am Zug.

    wenn ich das richtig verstehe, und Spieler1 eine 12 zieht, dann werden Spieler2 die Zahlen (Teiler) 1,2,3,4,6 also in Summe 16 gutgeschrieben....

  • Hi

    wenn ich das richtig verstehe, und Spieler1 eine 12 zieht, dann werden Spieler2 die Zahlen (Teiler) 1,2,3,4,6 also in Summe 16 gutgeschrieben....

    Zitat

    Spieler 1: Zieht nur Primzahlen.

    Er kann keine 12 ziehen. Allerdings halten sich dann die Teiler in Grenzen :D

  • Zitat von "Andy"

    [...] wenn ich das richtig verstehe, und Spieler1 eine 12 zieht, dann werden Spieler2 die Zahlen (Teiler) 1,2,3,4,6 also in Summe 16 gutgeschrieben....

    Das hast du so richtig verstanden. Allerdings werden nur die Teiler gutgeschrieben, die sich noch in der Zahlenmenge befinden.

    Ich war mal so frei und habe eine Simulation des Spieles programmiert. Über den Programmierstil lässt sich sicherlich streiten, aber es funktioniert soweit. Ich habe 4 Algorithmen eingebaut:
    Spieler 2: Zieht Primzahlen wenn vorhanden oder kleinste Zahl.
    Spieler 3: Zieht Random Zahlen
    Mein erster KI Vorschlag
    Mein zweiter KI Vorschlag

    Spieler 1 habe ich nicht eingebaut, weil es nicht möglich ist immer NUR Primzahlen zu ziehen. Irgendwann sind alle Primzahlen weg. Und bei Spieler 4 wusste ich keinen Ansatz.

    Ich habe folgende Funktionsnamen vergeben:
    _PlayerOne (Spieler 2)
    _PlayerTwo (Spieler 3)
    _MyKIOne (erster KI Vorschlag)
    _MyKITwo (zweiter KI Vorschlag)

    Ich habe die Algo's gegeneinander antreten lassen. Ich habe _PlayerTwo ganz bewusst weggelassen damit kein Zufall den Spielfluss stören kann. Den Algo den ich zuerst immer nenne hat in dem Spiel begonnen. Hier die Ergebnisse bei einem Spiel von 1 bis 20 Zahlen:

    Spoiler anzeigen

    Das verwundert mich gerade ein wenig... (Kann aber auch sein das ich iwo nen Fehler in den Algo's drin hab ^^)
    Aber ich würde mal schätzen wenn man eine Kombination aus den Algo's bauen würde, dann wäre diese noch besser.
    Ich selber habe noch keine weitere Idee...

    Hier das Skript zum ausprobieren:

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $i, $aiNumbers[10], $bPlayer, $iIndex, $aiPoints[2]

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($aiNumbers) - 1
    $aiNumbers[$i] = $i + 1
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    $bPlayer = Random(0, 1, 1)

    [/autoit] [autoit][/autoit] [autoit]

    While UBound($aiNumbers)
    Switch $bPlayer
    Case True ; One
    ;~ $iIndex = _PlayerOne($aiNumbers)
    ;~ $iIndex = _PlayerTwo($aiNumbers)
    ;~ $iIndex = _MyKIOne($aiNumbers)
    ;~ $iIndex = _MyKITwo($aiNumbers)
    Case False ; Two
    ;~ $iIndex = _PlayerOne($aiNumbers)
    ;~ $iIndex = _PlayerTwo($aiNumbers)
    ;~ $iIndex = _MyKIOne($aiNumbers)
    ;~ $iIndex = _MyKITwo($aiNumbers)
    EndSwitch

    [/autoit] [autoit][/autoit] [autoit]

    $aiPoints[Not $bPlayer] += _GetPoints($aiNumbers, $iIndex, True)
    $aiPoints[$bPlayer] += _GetPoints($aiNumbers, $iIndex)
    $bPlayer = Not $bPlayer
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite('> One: ' & $aiPoints[0] & @CRLF & _
    '! Two: ' & $aiPoints[1] & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    ; Zieht größte Primzahl
    ; Wenn nicht vorhanden dann kleinste Zahl
    Func _PlayerOne($aiNumbers)
    Local $i, $n, $b

    [/autoit] [autoit][/autoit] [autoit]

    For $i = UBound($aiNumbers) - 1 To 1 Step - 1
    For $n = $aiNumbers[$i] - 1 To 1 Step - 1
    If IsInt($aiNumbers[$i] / $n) Then $b = True
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If Not $b Then Return $i
    $b = False
    Next
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; Zieht eine zufällige Zahl
    Func _PlayerTwo($aiNumbers)
    Return Random(0, UBound($aiNumbers) - 1, 1)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    ; Zieht größt mögliche Zahl ohne Teiler in der Zahlenmenge
    Func _MyKIOne($aiNumbers)
    Local $i, $n, $b

    [/autoit] [autoit][/autoit] [autoit]

    For $i = UBound($aiNumbers) - 1 To 1 Step - 1
    For $n = $i - 1 To 0 Step - 1
    If IsInt($aiNumbers[$i] / $aiNumbers[$n]) Then $b = True
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If Not $b Then Return $i
    $b = False
    Next
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; Zieht die Zahl welche den größten Punktevorteil erbringt
    Func _MyKITwo($aiNumbers)
    Local $i, $aiTeiler, $n, $iTempP, $iSaveP, $iSaveI

    [/autoit] [autoit][/autoit] [autoit]

    For $i = UBound($aiNumbers) - 1 To 1 Step - 1
    Dim $aiTeiler[1]
    For $n = $i - 1 To 0 Step - 1
    If IsInt($aiNumbers[$i] / $aiNumbers[$n]) Then
    _ArrayAdd($aiTeiler, $aiNumbers[$n])
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    $iTempP = $aiNumbers[$i]
    For $n = 1 To UBound($aiTeiler, $n) - 1
    $iTempP -= $aiTeiler[$n]
    Next

    [/autoit] [autoit][/autoit] [autoit]

    If $iTempP > $iSaveP Then
    $iSaveI = $i
    $iSaveP = $iTempP
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    Return $iSaveI
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    ; ++++++++++ +++++++++ ++++++++ +++++++ +++++ ++++ +++ ++ +

    [/autoit] [autoit][/autoit] [autoit]

    Func _GetPoints(ByRef $aiNumbers, ByRef $iIndex, $bTeiler = False)
    Local $iReturn, $i

    [/autoit] [autoit][/autoit] [autoit]

    If Not $bTeiler Then
    $iReturn = $aiNumbers[$iIndex]
    _ArrayDelete($aiNumbers, $iIndex)
    Return $iReturn
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    While $i < $iIndex
    If IsInt($aiNumbers[$iIndex] / $aiNumbers[$i]) Then
    $iReturn += $aiNumbers[$i]
    _ArrayDelete($aiNumbers, $i)
    $iIndex -= 1
    $i -= 1
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $i += 1
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    Return $iReturn
    EndFunc

    [/autoit]
  • Ich würde mit Alpha-Beta-Pruning 4 Züge tief berechnen, und die Zugfolge wählen, die die größte Differenz zwischen mir und dem Gegner ergibt. jedoch habe ich keine Ahnung wie ich diese Algorithmus implementieren kann. Jemand ne Ahnung ?