Connect4

Connect4 is a HackaGames game, A simple two-player game where each player tries to align 4 of their pieces.

Try the game:

HackaGames comes with a command to start and try the game with an interactive interface in a shell.

hacka-connect4-play

The player can perform one and only one action at its turn, and the game stops automatically with a winner or when no more pieces can be set on the grid.

The actions consist of positioning a player's piece on one of the grid columns. There are 7 possible actions: A, B, C, D, E, F and G for the corresponding column.

Example of the game at some point:

  A   B   C   D   E   F   G
|   |   |   |   |   |   |   |
|   |   |   |   |   |   |   |
|   |   |   |   |   |   |   |
|   |   |   | O |   |   |   |
|   |   | X | X |   |   |   |
| X | O | O | O |   |   | X |
-----------------------------
you: O

First player play 'O' and the second 'X'. The first player aligning 4 of its pieces, in any direction win the game.

However, it is also possible to edit and execute a short script launching a game with the interactive shell-interface, and a very simple opponent.

from hackagames.connect4 import Connect4Master
from hackagames.connect4.shell import PlayerShell
from hackagames.connect4.firstBot import Bot

gameMaster= Connect4Master()
gameMaster.launchLocal( [PlayerShell(), Bot()], 1 )

Initialize a Bot

If you are implementing your first bot, please follow the first bot tutorial on Py421 game.

The wakeUp method informs essentially on the player identifier (\(1\) or \(2\), \(1\) play first), the perception of the status of the grid. The hackagames Connect4 package includes a very useful Grid class to manipulate the game state.

A minimal random Bot can be implemented in a few lines:

from hacka.games.connect4.grid import Grid
import random

class MyBot :
    def __init__(self):
        super().__init__()
        self.grid= Grid()
        self.playerId= 0
        self.results= []

    # Player interface :
    def wakeUp(self, playerId, numberOfPlayers, gamePod):
        self.playerId= playerId
        assert( gamePod.label() == 'Connect4')

    def perceive(self, gameState):
        # update the game state:
        self.grid.initializeFrom( gameState )
        # print(self.grid)

    def decide(self):
        options = self.grid.possibilities()
        action = random.choice( options )
        return hacka.Pod(action)

    def sleep(self, result):
        self.results.append(result)

And can be tested by replacing the PlayerShell or the opponent bot in the launch script. To notice that, the number of games can be increased mainly when 2 bots are competing:

gameMaster= Connect4Master()
gameMaster.launchLocal( [MyBot(), Bot()], 1000 )

At this point, the results comparing the 2 Bot would be very close. The 2 bots have the same behavior. To notice that, the list of actions do not filter positions already taken. To notice that, in case of wrong actions, the game will ask the player for a second call. By providing the same answer, the master and the bot can enter in an infinite loop.

Customaize your Bot:

You can explore the grid to select the best action possible. Some useful Gird methods:

  • columnSize(self): The number of columns (7 in classical configuration)
  • heightMax(self): The height of the grid (6 position per column in classical configuration)
  • height(self, iColumn): the actual height of the iColumn column (considered the played piece in this column)
  • column(self, iColumn): a list of integers modeling the iColumn column pieces (0 no player, 1 player 1 and 2 player 2)
  • position(self, c, h): The value at column c height h (0, 1 or 2).

To test a move you will need

  • copy(self): returning a deep copy of the grid (before to make some changes)
  • playerPlay(self, iPlayer, aLetter): to alter the grid considering that player iPlayer play on the aLetter column.
  • winner(self): returns the player Id winning the game if 4 of his pieces are aligned.
  • possibilities(self): returns the list of possible move (letters), if the columns are not full.

To notice that you can move from column letter to the column identifier and vice versa with chr (a char from an integer) and ord (the interger code of a char). For instance:

iColumn= ord(aLetter)-ord('A')
aLetter= chr( ord('A')+iColumn )

The source code is provided on github.com.