Learn VB .NET Through Game Programming [Electronic resources]

Matthew Tagliaferri

نسخه متنی -صفحه : 106/ 43
نمايش فراداده

Developing Rainbow Life

Conway’s Game of Life is popular enough that people have come up with variations of it. Some variations tinker with the neighbor counts that determine a cell’s state. Others take the two-dimensional lattice of cells into the third dimension and create a three-dimensional version of life with cubes representing the cells. One particularly interesting Life variation is a multicolored version, called Color Life or Rainbow Life. In this version, each living cell contains a color property, and the cell is rendered in that color. When a new cell is born, the color of that cell is determined by the average color of the three living neighbor cells that made its birth possible. This lends a sort of parent-child relationship to the game because the three “parent” cells give birth to a new “child” that inherits a trait from all three of them—the trait of color.

An example will shed some further light on the issue. Figure 5-4 shows a grid of cells. In the next clock tick, the center cell will spring to life because it has three living neighbors. Suppose that neighbor 1 is pure red, neighbor 2 is pure blue, and neighbor 3 is white. The center cell’s color will inherit from its three parents by averaging their colors together. Table 5-1 shows the red/green/blue components of these cells.

Figure 5-4: The Rainbow Life game

Table 5-1: Cell Color Averaging in Rainbow Life

NEIGHBOR

COLOR

RED

GREEN

BLUE

1

Red

255

0

0

2

Blue

0

0

255

3

White

255

255

255

Result

--

170

85

170

You obtain the result by adding the three color components and dividing by 3. RGB color 170, 85, 85 is a medium brown. This is the color that the newborn cell will have. Like its one-color cousin, watching Rainbow Life in action gives you far more insight into how it works than reading an explanation of it. Have fun exploring the sample program by simply running it before studying the code.

Creating the RainbowLifeGame Class

Because Rainbow Life is so similar to its monochromatic cousin, the best way to implement this game is to inherit directly off of the two Life classes discussed previously. The game class is the easier of the two because you need to change only one method to achieve the rainbow functionality. That member is the CreateOneCell method, which you’ll recall is the method that’s called polymorphically on the base class to return an instance of the game-specific cell descendant. Listing 5-10 shows the new code for the Rainbow Life variation of this method.

Listing 5.10: Rainbow Life CreateOneCell Method

Protected Overrides Function _ 
CreateOneCell(ByVal oPos As Point) As CellularAutomataCell 
Dim oC As RainbowLifeCell 
oC = New RainbowLifeCell(oPos, Me.CellRadius) 
If oRand.Next(0, Int32.MaxValue) Mod 5 = 0 Then 
oC.Alive = True 
oC.SetRandomColor() 
End If 
Return oC 
End Function 

Like the single-color version, Rainbow Life creates a living cell 20 percent of the time. This newly born cell is also given a random color using the SetRandomColor method on the cell class.

What’s cool about inheriting the rainbow version of the Life game class from the standard Life game class is that you don’t have to change the functionality of anything except the method you’ve just seen. The RunAGeneration method, for instance, needs no changing because you want the same actions to occur. Those actions (to give you a refresher) are to reset all the cells’ neighbor counts to 0, then to rip through all the living cells and update their neighbor counts, and finally to change the state of each cell according to its neighbors.

What does change in the rainbow version is what’s going on in some of these subparts. For example, when updating neighbor counts, the program also needs to keep track of the colors of all the neighbors so that a newly born cell can determine what its new color should be. The cell class declares all of this new functionality, however.

Creating the RainbowLifeCell Class

The cell class for the rainbow version of Life also inherits from the “standard” Conway version of the CellularAutomataCell class, but there are quite a few more changes. The first change was already hinted at earlier—a method named SetRandomColor that picks a starting color for each cell. Listing 5-11 displays that method, which chooses the color of each cell at the start of the game, along with some other members.

Listing 5.11: The Method SetRandomColor

Private FColor As Color 
Private FNeighborRTot As Integer 
Private FNeighborGTot As Integer 
Private FNeighborBTot As Integer 
Public Sub SetRandomColor() 
Dim c As Color 
Select Case oRand.Next(0, Int32.MaxValue) Mod 10 
Case 0 : c = Color.Yellow 
Case 1 : c = Color.Green 
Case 2 : c = Color.Blue 
Case 3 : c = Color.Red 
Case 4, 5 : c = Color.White 
Case 6 : c = Color.Orange 
Case 7 : c = Color.Violet 
Case 8 : c = Color.DarkBlue 
Case 9 : c = Color.Magenta 
End Select 
Me.SetColor(c) 
End Sub 
Overrides Function GetColor() As Color 
Return IIf(Not Alive, Color.Black, FColor) 
End Function 
Private Sub SetColor(ByVal c As Color) 
FColor = c 
End Sub 

This method doesn’t implement a truly random color (selecting random numbers from 0–255 for red, green, and blue and constructing a color from these components). The technique in this method, which is to choose from nine “base” colors (including white), gives a much more interesting starting state and much prettier results once the cells start on their life cycles.

Also shown in Listing 5-11 is the GetColor method, which overrides the original member found in the CellularAutomataCell class (this method is declared MustOverride in the ancestor). This method returns the private FColor variable if the current cell is alive and black if the cell is dead. You’ve also implemented a SetColor method to set the private color variable. The more common way of implementing code to get and set a private variable is to declare a property, but you can’t do that in this case because you were already forced to implement a GetColor method by the ancestor class. Implementing a Color property would be redundant to this existing method.

The remaining private variables, named FNeighborRTot, FNeighborGTot, and FBeighborBTot, store the color components of living neighbor cells for the purpose of creating a new offspring with the arithmetic mean of the new cell’s parents.

Listing 5-12 shows how the program uses these color-counting variables. First, it resets them to the value 0 in the overridden PreCountReset method. This method is called before neighbor counting begins in each clock tick iteration of the game. Note how the method calls the ancestor method using the keyword MyBase, meaning that this method is extending the functionality defined in the ancestor class.

Listing 5.12: Overridden Neighbor-Counting Classes

Public Overrides Sub PreCountReset() 
MyBase.PreCountReset() 
FNeighborRTot = 0 
FNeighborGTot = 0 
FNeighborBTot = 0 
End Sub 
Public Overrides Sub UpdateNeighborsBasedOnMe() 
Dim oC As RainbowLifeCell 
If Me.Alive Then 
For Each oC In FNeighbors 
oC.NeighborCount += 1 
oC.FNeighborRTot += Me.GetColor.R 
oC.FNeighborGTot += Me.GetColor.G 
oC.FNeighborBTot += Me.GetColor.B 
Next 
End If 
End Sub 
Public Overrides Sub UpdateAliveBasedonNeighbors() 
Dim oC As ConwaysLifeCell 
If Not Alive Then 
If NeighborCount = 3 Then 
Alive = True 
Me.SetColor(Color.FromArgb(_ 
FNeighborRTot \ 3, _ 
FNeighborGTot \ 3, _ 
FNeighborBTot \ 3)) 
End If 
Else 
'alive now, stays alive w/ 2 or 3 neighbors 
Alive = (NeighborCount = 2 Or NeighborCount = 3) 
End If 
End Sub 

The actual neighbor counting happens in the method UpdateNeighborsBasedOnMe, which is also overridden from the ancestor class. Because no MyBase call exists here, this method totally replaces the functionality in the base class. If the cell is currently alive, then the neighbor count of all neighbor cells is incremented by 1 (like the standard Life class), and the color component’s counting variables are also incremented by the red, green, and blue components of this cell’s current color.

The final method, UpdateAliveBasedOnNeighbors, runs after all the neighbor counting. This method is similar to the version it overrides in the standard Life game. If a living cell has two or three neighbors, it remains alive. If a dead cell has exactly three neighbors, it springs to live and its starting color is set to the average color of the three neighbor cells. (The summed value of the three red, green, and blue components of the three neighbors are calculated during the counting process—all you need to do here is to divide those values by 3.)

That wraps it up for the Rainbow Life variant. By inheriting off of the base Life class, you were able to change the functionality enough to create a whole new game.