Skip to main content

pyGameAIFoundation is a python package that provides classes for implementing AI in games

Project description

pyGameAIFoundation

Source code: GitHub

pyGameAIFoundation is a python package that provides classes for implementing decision making AI in games. It includes classes for goal-oriented behavior, action execution, and blackboard architecture. A develper can use these foundation classes to implement game goals and actions specific to their game, and to utilize those objects to create goal-based behavior in a game.

Credit where credit is due

  • The package generally implements pseudo code from the book "Artificial Intelligence for Games", Second Edtion, by Ian Millington and John Funge, Chapter 5, 2009.

Gob class

The Gob, which is an implementation of Goal-Oriented Behavior. Specifically, it implements the Simple Selection mechanisim for choosing a game action from a set of goals and actions. It implements pseudo code from Section 5.7.2 of "Artificial Intelligence for Games".

GameGoal class

GameGoal is an abstract base class. Concrete implementations of this class represent goals that can be achieved in a game. It implements pseudo code from Section 5.7.2-3 of "Artificial Intelligence for Games".

A concrete implementation must implement the method getInsistence(), which is called to get the insistence (urgency or importance) value of the goal. The higher the insistence value, the more urgent or important the goal is.

ActionManager class

The ActionManager class manages the scheduling and execution of actions in a game. It implements pseudo code from Section 5.11.3 of "Artificial Intelligence for Games".

GameAction class

GameAction is an abstract base class, GameAction. Concrete implementations of this class represent actions that can be performed in a game. It generally implements pseudo code from Section 5.11.4 of "Artificial Intelligence for Games", The possibility of actions being able to read and write data to a blackboard is additionally included, following the blackboard architecture pattern described in Section 5.9 of the same book.

Concrete implementation child classes of GameAction must:

  • Implement the method execute(), which is called to enact the specific behavior of the action.
  • Override the method isComplete(), which is called to determine if the action is completed.

Concrete implementation child classes of GameAction also:

  • May override the method canInterrupt(), which is called to determine if this action can interrupt other actions, e.g., in the active collection of an ActionManager object.
  • May override the method canDoBoth(...), which is called to determine of this action can be executed at the same time as another action, e.g., in the active collection of an ActionManager object.
  • Should override the method getGoalChange(...), which is called to determine how this action affects the insistence of a specific goal, e.g., in the goal collection maintained by a Gob object. The method should return a negative integer, indicating a reduction in the insistence of the goal when the action is executed.

GameActionCombination class (child of GameAction)

The GameActonCombination class is a child of GameAction, and it represents a combination of game actions that can be performed together (in parallel, at least in the sense of game logic).

GameActionSequence class (child of GameAction)

The GameActionSequence class is a child of GameAction, and it represents a sequence of game actions that must be performed in order in the sense of game logic.

Usage

# Local imports
from pyGameAIFoundation.gob import Gob
from pyGameAIFoundation.game_goal import GameGoal, GoalInsistence
from pyGameAIFoundation.action_manager import ActionManager
from pyGameAIFoundation.game_action import GameAction, GameActionCombination, GameActionSequence


# Create a couple of concrete GameGoal subclasses with different insistence values.

class GoalOne(GameGoal):
    def getInsistence(self):
        """
        Return the insistence value of the goal.
        :return: The insistence value of the goal, int
        """
        return GoalInsistence.LOW


class GoalTwo(GameGoal):
    def getInsistence(self):
        """
        Return the insistence value of the goal.
        :return: The insistence value of the goal, int
        """
        return GoalInsistence.MEDIUM


# Create a pair of GameAction subclasses with different implementations of getGoalChange(...)

class ActionOne(GameAction):
    def __init__(self, expiry_time=0, priority=0, read_blackboard=None, write_blackboard=None):
        """
        :parameter expiry_time: The time in an arbitrary count-up from zero until the action expires, as int
        :parameter priority: The priority of the action. Higher numbers indicate higher priority. As int
        :parameter read_blackboard: A function to read from the blackboard, as callable
            Signature: read_blackboard(key: str) -> any
        :parameter write_blackboard: A function to write to the blackboard, as callable
            Signature: write_blackboard(key: str, value: any) -> None
        """
        super().__init__(expiry_time, priority, read_blackboard, write_blackboard)
        self._canInterrupt = False
        self._canDoBoth = False
        self._completed=False

    def canInterrupt(self):
        """
        Return whether this action can interrupt any other action.
        :return: True if the action can interrupt any other action, False otherwise, as boolean.
        """
        return self._canInterrupt

    def canDoBoth(self, other_action):
        """
        Return whether this action can be done at the same time as another action.
        :param other_action: The other action to check against, as GameAction object.
        :return: True if both actions can be done at the same time, False otherwise, as boolean.
        """
        assert(isinstance(other_action, GameAction))
        return self._canDoBoth

    def isComplete(self):
        """
        Return whether this action is complete.
        :return: True if the action is complete, False otherwise, as boolean.
        """
        return self._completed

    def execute(self):
        """
        Execute the action. In this case, by writing to the blackboard (if we have one) and
        setting the completed flag to True.
        :return: None
        """
        print("Executing ActionOne")
        if self._write_blackboard:
            self._write_blackboard('ActionOne Output', 15)
            print("Wrote to blackboard: ActionOne Output = 15")
        self._completed = True
        return None

    def getGoalChange(self, goal=None):
        """
        Return the goal insistence change associated with this action.
        :param goal: The goal to check against, as GameGoal object.
        :return: The goal insistence change associated with this action, as int.
        """
        assert(isinstance(goal, GameGoal))
        if isinstance(goal, GoalOne):
            return -GoalInsistence.MEDIUM
        else:
            return -GoalInsistence.LOW


class ActionTwo(GameAction):
    def __init__(self, expiry_time=0, priority=0, read_blackboard=None, write_blackboard=None):
        """
        :parameter expiry_time: The time in an arbitrary count-up from zero until the action expires, as int
        :parameter priority: The priority of the action. Higher numbers indicate higher priority. As int
        :parameter read_blackboard: A function to read from the blackboard, as callable
            Signature: read_blackboard(key: str) -> any
        :parameter write_blackboard: A function to write to the blackboard, as callable
            Signature: write_blackboard(key: str, value: any) -> None
        """
        super().__init__(expiry_time, priority, read_blackboard, write_blackboard)
        self._canInterrupt = False
        self._canDoBoth = False
        self._completed=False

    def canInterrupt(self):
        """
        Return whether this action can interrupt any other action.
        :return: True if the action can interrupt any other action, False otherwise, as boolean.
        """
        return self._canInterrupt

    def canDoBoth(self, other_action):
        """
        Return whether this action can be done at the same time as another action.
        :param other_action: The other action to check against, as GameAction object.
        :return: True if both actions can be done at the same time, False otherwise, as boolean.
        """
        assert(isinstance(other_action, GameAction))
        return self._canDoBoth

    def isComplete(self):
        """
        Return whether this action is complete.
        :return: True if the action is complete, False otherwise, as boolean.
        """
        return self._completed

    def execute(self):
        """
        Execute the action. In this case, by reading from the blackboard (if we have one) and
        setting the completed flag to True.
        :return: None
        """
        print("Executing ActionTwo")
        if self._read_blackboard:
            value = self._read_blackboard('ActionOne Output')
            print(f"Read from blackboard: ActionOne Output = {value}")
        self._completed = True
        return None

    def getGoalChange(self, goal=None):
        """
        Return the goal insistence change associated with this action.
        :param goal: The goal to check against, as GameGoal object.
        :return: The goal insistence change associated with this action, as int.
        """
        assert(isinstance(goal, GameGoal))
        if isinstance(goal, GoalTwo):
            return -GoalInsistence.MEDIUM
        else:
            return -GoalInsistence.LOW

        
# Create the Gob (goal-oriented behavior) object. 
gob = Gob()
# Add goals to the Gob.
gob.add_goal(GoalOne('goal_1'))
gob.add_goal(GoalTwo('goal_2'))
# Create actions and add them to the Gob.
act1 = ActionOne(expiry_time=10)
act2 = ActionTwo(expiry_time=10)
gob.add_action(act1)
gob.add_action(act2)
    
# Create the ActionManager.
mgr = ActionManager()

# Choose the best action to execute.
(bestAct, topGoal) = gob.chooseAction()
bestAct.expiry_time = mgr.currentTime + bestAct.expiry_time  # Set the expiry time for the action.
print(f'Chose action with priority {bestAct.priority} to fulfill goal "{topGoal.name}" with insistence {topGoal.getInsistence()}')

# Schedule the best action in the ActionManager.
mgr.scheduleAction(bestAct)

# Output the action manager state.
print(f'Action Manager State before execution:\n{mgr}')

# Execute the action manager, which will activate the scheduled action and execute it
mgr.execute()
# Output the action manager state.
print(f'Action Manager State after first execution:\n{mgr}')
# Execute the action manager again, which will remove the completed action from the active list.
mgr.execute()
# Output the action manager state.
print(f'Action Manager State after second execution:\n{mgr}')

# Create a GameActionCombination and demonstrate its use in the Gob and ActionManager.
combo = GameActionCombination(expiry_time=10, combo_acts=[act1, act2])
gob.add_action(combo)
(bestAct, topGoal) = gob.chooseAction()
bestAct.expiry_time = mgr.currentTime + bestAct.expiry_time  # Set the expiry time for the action.
print(f'\nChose action with priority {bestAct.priority} to fulfill goal "{topGoal.name}" with insistence {topGoal.getInsistence()}')
mgr.scheduleAction(bestAct)
print(f'Action Manager State before execution:\n{mgr}')
# Activate the scheduled combination action, and execute both actions in the combo.
mgr.execute()
print(f'Action Manager State after first execution:\n{mgr}')
# Remove the completed combination action from the active list.
mgr.execute()
print(f'Action Manager State after second execution:\n{mgr}')
# Remove the combination action from the Gob's action list, so it doesn't interfere with the rest of the demo,
# since it's insistence change is the same as the sequence action created next.
gob.remove_action(combo)

# Create a GameActionSequence, demonstrate its use in the Gob and ActionManager, and use of the
# blackboard to pass information between actions in a sequence.
seq = GameActionSequence(expiry_time=10)
act1 = ActionOne(expiry_time=10, read_blackboard=seq.readFromBlackBoard, write_blackboard=seq.writeToBlackBoard)
act2 = ActionTwo(expiry_time=10, read_blackboard=seq.readFromBlackBoard, write_blackboard=seq.writeToBlackBoard)
seq.addAction(act1)
seq.addAction(act2)
gob.add_action(seq)
(bestAct, topGoal) = gob.chooseAction()
bestAct.expiry_time = mgr.currentTime + bestAct.expiry_time  # Set the expiry time for the action.
print(f'\nChose action with priority {bestAct.priority} to fulfill goal "{topGoal.name}" with insistence {topGoal.getInsistence()}')
mgr.scheduleAction(bestAct)
print(f'Action Manager State before execution:\n{mgr}')
# Activate the scheduled sequence action, and execute the first action in the sequence.
mgr.execute()
print(f'Action Manager State after first execution:\n{mgr}')
# Execute the second action in the sequence.
mgr.execute()
print(f'Action Manager State after second execution:\n{mgr}')
# Remove the completed sequence action from the active list.
mgr.execute()
print(f'Action Manager State after third execution:\n{mgr}')

Demonstration

To run the demonstration, type python -m pyGameAIFoundation.main in a terminal window. Note, that this assumes that the pyGameAIFoundation package has been installed in your Python environment. The demonstration is essentially the same as the usage example above.

Unittests

Unittests for the pyGameAIFoundation are in the tests directory, with filenames starting with test_. To run the unittests, type python -m unittest discover -s ..\..\tests -v in a terminal window in the src\pyGameAIFoundation directory.

License

MIT License. See the LICENSE file for details

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pygameaifoundation-0.1.1.tar.gz (20.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pygameaifoundation-0.1.1-py3-none-any.whl (19.2 kB view details)

Uploaded Python 3

File details

Details for the file pygameaifoundation-0.1.1.tar.gz.

File metadata

  • Download URL: pygameaifoundation-0.1.1.tar.gz
  • Upload date:
  • Size: 20.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pygameaifoundation-0.1.1.tar.gz
Algorithm Hash digest
SHA256 30100bb5379215038b0bbfc7d466637901e18396465a31ba7cc2cf394e934b2c
MD5 5c6d21a2bbc17300040843064ef18439
BLAKE2b-256 4f9418621c41572fd58c14cb2351b42ffbbdf3cd36ff6e344f6420d66116419a

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygameaifoundation-0.1.1.tar.gz:

Publisher: release.yml on KevinRGeurts/pyGameAIFoundation

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pygameaifoundation-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pygameaifoundation-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4b21b7b14e6df74341dcf418ded3098823c3b3b6db3eba20031b870c02dbd284
MD5 cbee6bc2615545d3a1a1e03b6ffb3f33
BLAKE2b-256 7eb2c3189df57527f018c969e18fa63f2b9de1f872c7981f4aa4d0f5c00f1245

See more details on using hashes here.

Provenance

The following attestation bundles were made for pygameaifoundation-0.1.1-py3-none-any.whl:

Publisher: release.yml on KevinRGeurts/pyGameAIFoundation

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page