Game Creation in Python

The ideas here is to present step by step the game creation in python with hackapy.

Reminder, hackapy is the HackaGames python librairy helping for the game and players to communicate together.

Directory structure

In your workspace directory create a subdirectory gameXyz where Xyz identify your new game (gameHello for instance). This new subdirectory (your working directory) will also include another subdirectory gameEngine regrouping the source code making your game working.

Directory squeletom:

gameHello                # your game folder
  - gameEngine              # sourcecode of the game

Then we start with 3 files:

  • README.md : a Markdown readme first file presenting the game and the rules.
  • gameEngine/__init__.py : a classical python files marking the entrance of your gameEngine package.
  • start-server : a start script, lauching the game server.

Game Engine

At minima gameEngine python package include a Game class deriving from hackapy.AbsGame in the __init__.py file. It is suppozed that the new Game class implement the abstract methods of AbsGame:

class AbsGame():

    # Game interface :
    def initialize(self):
        # Initialize a new game
        # Return the game configuration (as a PodInterface)
        # the returned Pod is given to player's wake-up method.
        pass

    def playerHand( self, iPlayer ):
        # Return the game elements in the player vision (a PodInterface)
        # the returned pod feed the player's perception method.
        pass

    def applyPlayerAction( self, iPlayer, action ):
        # Apply the action choosen by the player iPlayer.
        # Return a boolean at True if the player terminate its actions for the current turn.
        # False means player need to be activated again before step to another player or another game turn.
        pass

    def tic( self ):
        # called function at turn end, after all player played its actions. 
        pass

    def isEnded( self ):
        # must return True when the game end, and False else.
        pass

    def playerScore( self, iPlayer ):
        # return the player score for the current game (usefull at game ending)
        pass

A last abstract method is defined: play. This method is more an inside method re-defined in function of how the players are managed. Actually, two strategies are proposed:

  • AbsSequentialGame: Players are activated at turns. The perception of the game for the \(i\)-th player and a call to decide are sent after the \((i-1)\)-th player terminates its actions (applyPlayerAction( (i-1), "action" ) returns True).
  • AbsSimultaneousGame: (Work In Progress) Players are activated in a simultaneous way. All the players receive their perception of the game then all the players are requested for their actions. Generally action resolution in these kinds of games are resolved in the tic method.

Example of of the simple hello games:

First, initialize python file and import the hackapy package.

#!env python3
"""
HackaGame - Game - Hello 
"""
import sys

sys.path.insert( 1, __file__.split('gameHello')[0] )
import hackapy as hg

Then, the Game implement an abstract sequential game (each player play at turns). The Game methods to implemented are: initialize, playerHand, applyPlayerAction, isEnded and playerScore. Here the Hello game simply echo the player action in a terminal \(3\) times (method applyPlayerAction).

class GameHello( hg.AbsSequentialGame ) :

    # Game interface :
    def initialize(self):
        # initialize the counter and only say hello.
        self.counter= 0
        return hg.Pod( 'hello' )

    def playerHand( self, iPlayer ):
        # ping with the increasing counter
        return hg.Pod( 'hi', flags=[ self.counter ] )  

    def applyPlayerAction( self, iPlayer, action ):
        # print the receive action message. And that all.
        print( f"Player-{iPlayer} say < {action} >" )
        return True

    def tic( self ):
        # step on the counter.
        self.counter= min( self.counter+1, 3 )

    def isEnded( self ):
        # if the counter reach it final value
        return self.counter == 3

    def playerScore( self, iPlayer ):
        # All players are winners.
        return 1

A counter initialized in initialize method, count \(3\) game turn (i.e. after each player play at-turn) in tic method. Then the isEnded method will return True. The method playerHand informs the player about the counter status. Finaly, there is no winner and all player will end with a result at \(1\) (method playerScore).

Lets play

The start-server script will permit to lauch the game server. It only instancate a Game with a determined number of players then call the AbsGame start method.

#!env python3
"""
HackaGame - Game - Hello 
"""
from gameEngine import GameHello

game= GameHello()
game.start()

That it.

You can set your script executable (chmod +x ./gameHello/start-server) and play with your new game:

# In a first shell:
./gameHello/start-server

# In a second shell:
./hackagames/connect-shell

Game initialization:

AbsGame initialization need a number of players. By default the value is on 1. However, if you want to fix this number or if you want to add extrat initialization, you need to call the parent initialization in your own.

As reminder in Python, initialization method is named __init__ and super() function provides an access to parent methods.

For instance, for a 2 player game:

class MyGame( hg.AbsSequentialGame ) :
    # Initialization:
    def __init__(self) :
        super().__init__( numberOfPlayers=2 )
        self._myAttribut= "Some initializations"

Going futher: Command Interpreter:

from hackapy.command import Command, Option

# Define a command interpreter: 2 options: host address and port:
cmd= Command(
        "start-server",
        [
            Option( "port", "p", default=1400 ),
            Option( "number", "n", 2, "number of games" )
        ],
        (
            "star a server fo gameConnect4 on your machine. "
            "gameConnect4 do not take ARGUMENT."
        ))
# Process the command line: 
cmd.process()
if not cmd.ready() :
    print( cmd.help() )
    exit()

...

game.start( cmd.option("number"), cmd.option("port") )

Going futher: Test-Driven:

You can start with a first : test_01_AbsGame script in a test directory.

to verify the call to HackaGames Games methods...

"""
Test - Hello Games Class
"""
import sys

sys.path.insert( 1, __file__.split('gameHello')[0] )
import hackapy as hg
import gameHello.gameEngine as ge

def test_gameMethod():
    game= ge.GameHello()

    assert( type( game.initialize().asPod() ) is hg.Pod  )
    assert( type( game.playerHand(1).asPod() ) is hg.Pod )
    assert( game.applyPlayerAction( 1, "sleep" )  )
    game.tic()
    assert( not game.isEnded() )
    assert( game.playerScore(1) == 1 )

...