TransWikia.com

How might I go about writting some AI for "Finger Wars"

Stack Overflow Asked on December 13, 2021

Please excuse this post if it doesn’t suit you. Maybe I shouldn’t ask this sort of question.

I don’t often post, however I was writting some code with my son. We codded a simple "Fingers War" game. I wasn’t familiar with this but I soon understood the elementary algorithm. I know we code a little verbose but it’s easier to read and understand when you are learning basic code.

Usually you play this facing another player in a playground. Both players tart with their hands showing one finger each. Player1 one can either pass a (or some) finger(s) to his other hand or touch his opponents hand thus adding the same amount of fingers there. If a hand gets to more than five fingers you substarct five and keep on playing. If one hand gets down to 0 you can still add fingers to it. However when opponents hands both get down to 0 its a win for player with fingers left.

You can try the game by running my code: Player1 hands are A & B Player2 hands are C & D. Player1 starts.

My question is "How might I go about codding the ‘Play against computer’?" I have no idea where to start. I have just this "def ai(self): pass" in my class. What might I put in it?

cheers to anyone how takes up the challenge and teaches us something,
kind regards,
Quinkink

Here is the code so far.

class FingerWars:
    
    hands = {'A': 1, 'B': 1, 'C': 1, 'D': 1}
    players = {'player1': 'Player1', 'player2': 'Player2'}
    player = 'player2'
    loop = True

    source_hands_str = {'player1': 'A or B', 'player2': 'C or D'}
    target_hands_str = {'A': 'B, C or D', 'B': 'A, C or D', 'C': 'A, B or D', 'D': 'A, B or C'}

    def __init__(self, player1='Player1', player2='Player2'):
        """
        Set player names or leave default
        :param player1: 
        :param player2: 
        """
        self.players['player1'] = player1
        self.players['player2'] = player2

    def game(self):
        """
        Main game loop.
        Toggle player turns.
        :return: void
        """
        while self.loop:
            if self.player == 'player1':
                self.player = 'player2'
            else:
                self.player = 'player1'
            self.fingers()
            if self.players[self.player] == 'computer':
                self.ai()
            else:
                self.turn()
            self.evaluate()
    
    def ai(self):
        """
        AI is called when one of the players is called 'computer'.
        How on earth might this work?
            LEVEL ONE: challenge
            LEVEL TWO: unbeatable
        :return: void
        """
        pass

    def turn(self):
        """
        Game turn.
        Can player only play from one hand?
        User specified target.
        Auto or user specified number of fingers.
        Do the math.
        :return: void
        """
        # AUTO CHOOSE IF ONE HAND = 0
        if self.player == 'player1':
            if self.hands['A'] == 0:
                source_hand = 'B'
                print(self.players[self.player] + 'source hand is : ' + source_hand)
            elif self.hands['B'] == 0:
                source_hand = 'A'
                print(self.players[self.player] + 'source hand is : ' + source_hand)
            else:
                source_hand = input(
                    self.players[self.player] + " choose a source hand (" + self.source_hands_str[self.player] + ")? ").upper()
        else:
            if self.hands['C'] == 0:
                source_hand = 'D'
                print(self.players[self.player] + 'source hand is : ' + source_hand)
            elif self.hands['D'] == 0:
                source_hand = 'C'
                print(self.players[self.player] + 'source hand is : ' + source_hand)
            else:
                source_hand = input(
                    self.players[self.player] + " choose a source hand (" + self.source_hands_str[self.player] + ")? ").upper()

        # SPECIFY TARGET HAND
        target_hand = input(
            self.players[self.player] + " choose a target hand (" + self.target_hands_str[source_hand] + ")? ").upper()
        fingers = self.hands[source_hand]

        # SPECIFY NB OF FINGERS IN INTERNAL FINGER SWAP
        if fingers > 1:
            if (source_hand in ('A', 'B') and target_hand in ('A', 'B'))
                    or (source_hand in ('C', 'D') and target_hand in ('C', 'D')):
                fingers = int(input(self.players[self.player] + " how many fingers? "))

        print(self.players[self.player] + " : " + source_hand + "  (" + str(self.hands[source_hand]) + ") -> " + target_hand)

        # DO THE TURN PLAY MATH
        if source_hand == 'A' or source_hand == 'B':
            if target_hand == 'A' or target_hand == 'B':
                self.hands[source_hand] = self.hands[source_hand] - fingers
                self.hands[target_hand] = self.hands[target_hand] + fingers
            else:
                self.hands[target_hand] = self.hands[target_hand] + fingers

        if source_hand == 'C' or source_hand == 'D':
            if target_hand == 'C' or target_hand == 'D':
                self.hands[source_hand] = self.hands[source_hand] - fingers
                self.hands[target_hand] = self.hands[target_hand] + fingers
            else:
                self.hands[target_hand] = self.hands[target_hand] + fingers

    def evaluate(self):
        """
        evaluates fingers in hand. Evaluates win.
        :return: void
        """
        # EVALUATE FINGERS IN HAND
        for key, value in self.hands.items():
            if value > 4:
                self.hands[key] = self.hands[key]-5
                
        # EVALUATE WINNER
        if self.hands['A'] == 0 and self.hands['B'] == 0:
            print('Player : ' + self.players[self.player] + ' wins the finger war!')
            self.fingers()
            self.loop = False
        if self.hands['C'] == 0 and self.hands['D'] == 0:
            print('Player : ' + self.players[self.player] + ' wins the finger war!')
            self.fingers()
            self.loop = False

    def fingers(self):
        """
        prints user feedback. This is called after each turn
        :return: void
        """
        feedback = "A:" + str(self.hands['A']) + " B:" + str(self.hands['B'])
            + " vs C:" + str(self.hands['C']) + " D:" + str(self.hands['D'])
        print(feedback)


def main():
    # game = FingerWars()
    game = FingerWars('Player1', 'Player2')
    game.game()


if __name__ == "__main__":
    main()

One Answer

To do this you need to build a model of the state space that the AI can use for searching. A good way to do this is using:

  • a data structure that encapsulates the current state of the game,
  • a function for getting the actions in the current state, and
  • function(s) for applying and undoing actions.

Given these functions, you can apply the minimax algorithm to forward simulate the game. But, there are a few complications:

  • You might not be able to search to the end of the game, so you might cut the search off at some depth.
  • Then, you'll need an evaluation function that says how good a state of the game is.
  • You might want to implement alpha-beta pruning to decrease the size of the tree.
  • You can also use iterative-deepening to dynamically figure out how deep to search.
  • Because there are duplicate states, you might want a transposition table to reduce the number of duplicate branches in the search tree.

An alternate approach is to just enumerate all possible state of the game (there aren't that many, relatively speaking) and to work backwards from proven states until you reach the start state. Then, you'll have an optimal strategy to play the game.

This should get you started - there are lots of questions about minimax, alpha-beta pruning, monte-carlo tree search and similar things here that can help you go deeper when you're ready.

Answered by Nathan S. on December 13, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP