Getting started with IronPython – Part 2: Let’s write some code

In the previous part I wrote on how I’ve leant the basics of IronPython programming. In this post I will show the first lines I wrote as part of the project I decided to write in order to learn IronPython.

Before I div into the code I’d like to take a moment to explain about the project I’m coding.

The Project

I’ve wanted to refresh some of the materials I’ve learnt at university in “Introduction to AI” course and for my IronPython project I choose to use Minimax algorithm. In case you’ve never heard of Minimax before - don’t worry, basically it’s an algorithm to choose the preferred move in a zero sum game of two players.

I’ll dedicate a post about it in the future but for now I’d like to cover the “prerequisite” for the algorithm – a working board game implementation.

Because I needed a simple board game with clear rules I choose to implement Mancala.

What is Mancala?

Mancala is a board game played by two players. This is what my Mancala board looks like:

 P1020979

Playing Mancala is easy:

If the last stone placed in the player’s store he gets another turn.

If the last stone is placed in an empty pot he captures the stones on the opposite pot and places them in his store along with the capturing stone.

And finally the game continues until one of the players clears all of his side (and cannot “move”).

Classes of the game

I have four classes in my Mancala  implementation: Vietnamese children playing - by ô ăn quan

  1. Board – has most of the game logic along with the board representation
  2. Store – represent the player’s stores and has a counter for the number of seeds in store
  3. Pot – represent a simple pot along with the amount of seeds in it
  4. MancalaException – game specific exception

Implementing MancalaException is simple, just define a new type that inherits from the Exception class:

image

This how I would have implemented a similar class in C#:

image

No big difference between there - both the implementations are short and to the point. Notice that I’ve use pass after the class definition tells IronPython not to expect anything more from the class.

Both Store and Pot classes are similarly implemented:

image 

In both classes the “cost” of creating a simple class is notably less then what we’ve used to from C#/VB.

Both classes has a simple “constructor” -  Python’s __init__, __init__ is one of Python’s special methods - For a full list of other special method used in Python (and IronPython) see Python Magic Methods Reference courtesy of Michael Foord.

Notice how we pass self to the class methods. Python uses the 1st parameter to access the current instance. In this example I’ve used self to set the initial number of seeds (4 in each pot and 0 in each store).

Because IronPython is a dynamic language I do not need to declare the “seeds” field before using it, in fact if I would have declared it in the class definition it would have been a static field which would have been the same for all instances for the class – this took some getting used to.  

The Board class

Board is the main class of our game. This class represents the game’s board and has the (very simple) game logic.

The Board class has one major method, and several small “helper” methods.

Let’s start with the constructor:

image

Because Python does not declare class fields before they’re used I’ve used the this method to create the board’s fields:

Python does not have the same for loops I’ve been used to have in other languages, in fact Python’s for is more similar to the foreach loop of C#/VB so in order to create a list of N pots I’ve used a trick:

  1. Create a list of N objects using the range keyword.
  2. Iterate on each object and do something.

The added bonus is the list created have N elements 0 to N-1 so I can implement a proper for loop this way

In this example I use for and range to iterate of a list of 6 elements and create a new pot class and add it to a list (line 9/10).

Board.Play method

The play method drives the game. It contains the main game logic and is responsible to act a single player turn:

image

Like all other the other methods it has the self parameter that represent the current instance (similar to this/me) and it also receives the pot that the player choose to move.

In line 4-5 I wrote simple check to make sure the player choose a valid pot (one that has seeds in it). Raising exception in Python is very similar to what I’m used to although passing is message was a bit different. In fact it seems that in Python I choose an exception type and a message and not create a new class with the message and an argument.

Lines 11 and 12 has the actual game play using the method __playSingleMove, this method marked by two underscores that tells Python that its a “private” method. this method cannot be called outside for the class and is used here to make play more readable.

I’ve used a PlayContext class to pass several arguments into this method, I needed to pass several arguments to the method that change each move. Instead of using this OO way I could have written the following:

image

Python can return multiple values but it seemed just wrong writing a line like that.

After all of the seeds have been distributed I have several more actions to do:

If the last seed has not been placed in the player store (14)

  1. Check if the last pot was empty and capture the opposite pot’s seeds (15-16)
  2. Change the current player to the next player (18)

If the current player cannot move – the game ends (20-21).

The code is readable I especially liked using indentation instead of curly brackets although it can be confusing at the beginning.

 

the full code of the Mancala classes is available here for download. This is the first time I’m writing Python/IronPython code so if I’ve missed anything or if you know of a better way to do something I did – please let me know either by email or by writing a comment.

 

That’s it for now – stay tuned for the next post where I’ll write about how I’ve unit tested my application.

image

[Mancala – by sinsofthedove]

Labels: