Click here to Skip to main content
Click here to Skip to main content
Go to top

Calcoolation: A Math Puzzle Board Game

, 6 Aug 2009
Rate this:
Please Sign up or sign in to vote.
Demo for a math puzzle board game

Cal-cool-ation

Introduction

"Calcoolation" is a puzzle game that at first looks like the popular Sudoku. Just like in Sudoku, you have an N x N matrix, where the digits 1 through N must be placed in each column and in each row without repetition. The difference lies in the fact that there are blocks of cells, named "cages", where specific operations involving the numbers in the cells must satisfy the specific result displayed in the cage.

Background

This game was invented by the Japanese teacher Tetsuya Miyamoto in 2004, under the name "KenKen" (Ken is the Japanese word for "cleverness"), although I only became aware of it in the beginning of this year. I got very impressed ("puzzled") by it, and then decided to develop the game in C#.

The most challenging part was to discover the correct strategy to pick the random numbers without repetitions in columns and rows. I decided to use the "Naked Pairs/Naked Triplets" strategy, which I borrowed from some sites dedicated to Sudoku solving. I'll discuss Naked Pairs later on in this article.

After picking all the numbers, I still had to randomly create the "cages". Cages are sets of contiguous cells in the board. I did this by randomly selecting pairs of cells in random directions, beginning from the top/left corner. Thus, initially the cages had two cells, but when a random cage cell superposes another cage cell, those cages are merged, so we could have 3-pieces and 4-pieces cages.

The Code

The code is divided into two layers: Core and WinUI. In the WinUI layer, we have the Windows Forms presentation logic. It's a very simple user interface, intended to make the player life easier. The important note here is that I created a user control ("CellBox") to hold the cell data, functionality, and events. In Windows Forms, user controls are useful tools for separation of concerns on the UI side.

The Core layer does most of the hard work. It's composed of classes that represent the three main entities in the game: the Board, the Cage, and the Cell. There can be only one Board in the game (that's why I decided to use the Singleton pattern). The default Board has the predefined dimension 4x4 (which can be changed later by the user). Each position in the board is held by a cell (that is, cell count = size²). Inside the board, the cells are also arranged in pieces called "Cages" (much like a traditional puzzle).

The pieces of code that I think worth mentioning are those related to random number picking, random cage formation, and game complete testing.

Random number picking

For random number picking, see the GenerateNumbers() method:

private void GenerateNumbers()
{
    ResetBoard();

    Random rnd = new Random();

    string number = "0";

    int minSize = size;
    int maxSize = 0;
    bool someSolved = true;

    while (someSolved)
    {
        someSolved = false;

        //Search for naked pairs in rows
        if (!someSolved)
        {
           //code removed for better visibility
        }

        //Search for naked pairs in columns
        if (!someSolved)
        {
           //code removed for better visibility
        }

        //Search for naked triplets in rows
        for (int row = 0; row < size; row++)
        {
           //code removed for better visibility
        }

        //Search for cells with a unique solution possible
        for (int row = 0; row < size; row++)
        {
           //code removed for better visibility
        }

        //Random selection
        if (!someSolved)
        {
           //code removed for better visibility
        }
    }
}

Notice that, according to the code above, the naked pairs are resolved in the beginning. Then, the naked triplets, and then the cells with a unique solution, and then the random selection. This is done so to avoid backtracking.

As a result, we now have a valid board, ready to be used:

Candidates

Random cage formation

The next important step is to randomly create the cages, and here is the GenerateCages method:

private void GenerateCages()
{
    cages = new List<Cage>();

    bool success = false;
    int orientation = 0;
    int c2 = 0;
    int r2 = 0;

    Random rnd = new Random();

    for (int r = 0; r < size; r++)
    {
        for (int c = 0; c < size; c++)
        {
            if (matrix[c, r].Cage == null)
            {
                success = false;
                while (!success)
                {
                    orientation = rnd.Next(1, 5);

                    switch (orientation)
                    {
                        case 1: // W
                            c2 = c - 1;
                            r2 = r;
                            break;
                        case 2: // E
                            c2 = c + 1;
                            r2 = r;
                            break;
                        case 3: // N
                            c2 = c;
                            r2 = r - 1;
                            break;
                        case 4: // S
                            c2 = c;
                            r2 = r + 1;
                            break;
                    }

                    if (c2 >= 0 && c2 < size && r2 >= 0 && r2 < size)
                    {
                        Cage cage = matrix[c2, r2].Cage;
                        if (cage == null)
                        {
                            cage = new Cage();
                            cage.CellList.Add(matrix[c2, r2]);
                            matrix[c2, r2].Cage = cage;
                        }
                        else
                        {
                            if (cage.CellList.Count > 3 && (c != size - 1 || r != size - 1))
                            {
                                continue;
                            }
                        }

                        cage.CellList.Add(matrix[c, r]);
                        matrix[c, r].Cage = cage;
                        cages.Add(cage);
                        success = true;
                    }
                }
            }
        }
    }

Starting from the {0,0} position on the board, and moving to the right and down directions, this function places pieces of two cells in random directions, and tests whether there is a conflict with an existent cage. In this case, the cages are merged; otherwise, a new cage is created:

Candidates

After that, the PickOperation() method chooses a possible random operation (picked between +, -, x, and ÷) using the numbers inside the cage.

public void PickOperation(Cage cage)
{
    bool success = false;

    while (!success)
    {
        if (currentOperation == 5)
            currentOperation = 1;

        switch (currentOperation)
        {
            case 1:
                cage.Operation = Operations.Plus;
                int sum = 0;
                foreach (Cell cell in cage.CellList)
                {
                    sum += Convert.ToInt32(cell.CellValue);
                }
                cage.Result = sum;
                success = true;
                break;
            case 2:
                cage.Operation = Operations.Minus;
                if (cage.CellList.Count == 2)
                {
                    int sub = Convert.ToInt32(cage.CellList[0].CellValue) - 
                              Convert.ToInt32(cage.CellList[1].CellValue);
                    if (sub > 0)
                    {
                        cage.Result = sub;
                        success = true;
                    }
                    else
                    {
                        sub = Convert.ToInt32(cage.CellList[1].CellValue) - 
                              Convert.ToInt32(cage.CellList[0].CellValue);
                        if (sub > 0)
                        {
                            cage.Result = sub;
                            success = true;
                        }
                    }
                }
                break;
            case 3:
                cage.Operation = Operations.Multiply;
                int mult = 1;
                foreach (Cell cell in cage.CellList)
                {
                    mult *= Convert.ToInt32(cell.CellValue);
                }
                cage.Result = mult;
                success = true;
                break;
            case 4:
                cage.Operation = Operations.Divide;
                if (cage.CellList.Count == 2)
                {
                    int quo = Convert.ToInt32(cage.CellList[0].CellValue) / 
                              Convert.ToInt32(cage.CellList[1].CellValue);
                    int rem = Convert.ToInt32(cage.CellList[0].CellValue) - quo * 
                              Convert.ToInt32(cage.CellList[1].CellValue);
                    if (rem == 0)
                    {
                        cage.Result = quo;
                        success = true;
                    }
                    else
                    {
                        quo = Convert.ToInt32(cage.CellList[1].CellValue) / 
                              Convert.ToInt32(cage.CellList[0].CellValue);
                        rem = Convert.ToInt32(cage.CellList[1].CellValue) - quo * 
                              Convert.ToInt32(cage.CellList[0].CellValue);
                        if (rem == 0)
                        {
                            cage.Result = quo;
                            success = true;
                        }
                    }
                }
                break;
        }

        currentOperation++;
    }
}

Getting Smarter: Playing With Candidates

I must confess that I found this game difficult for me. I can face a 3 x 3 board, but from 4 x 4 on, things get more complicated. If I had this game on paper, I would probably take notes about which numbers can or can't be placed in cells. Then I discovered the correct digit for a certain cell, I'd pick up my pencil to strike through that digit in the other cells in the same row and the same column. Then I had this weird idea: what if I allowed users to take notes directly in the application? And here it is, this new feature through the menu "Settings -> Show Candidates", where you can turn on/off the candidate digits:

Candidates

Notice that, when you play with candidates, the interface changes a little bit:

Candidates

The Naked Pairs

Before getting a random number for a cell, you should always look for "naked pairs". Naked pairs mean that in some row or column, there are two cells with two possible values. In the figure below, we can spot these naked pairs, with only two possible values [3,4]:

Candidates

The reason for spotting naked pairs is simple: since these two cells can hold only these two digits, no other cells in that row will have "3" or "4". Thus we can remove them from the possible digits:

Candidates

Points of interest

I think the harder part for me was to discover how to randomly arrange the numbers in a valid N x N board (that is, without repeating digits in rows and columns). After some research, I found the naked pairs / naked triplets technique I mentioned before. Of course, there are many more techniques I could have used, so if you are interested in them, not only as a developer, but also as a player, here it goes:

History

  • 2009-07-26: First post.
  • 2009-07-29: New feature: "Show Candidates".
  • 2009-07-30: New version for Visual Studio 2005.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Marcelo Ricardo de Oliveira
Software Developer
Brazil Brazil
Marcelo Ricardo de Oliveira is a senior software developer who lives with his lovely wife Luciana and his little buddy and stepson Kauê in Guarulhos, Brazil, is co-founder of the Brazilian TV Guide TV Map and currently works for ILang Educação.
 
He is often working with serious, enterprise projects, although in spare time he's trying to write fun Code Project articles involving WPF, Silverlight, XNA, HTML5 canvas, Windows Phone app development, game development and music.
 
Published Windows Phone apps:
 
 
Awards:
 
CodeProject MVP 2012
CodeProject MVP 2011
 
Best Web Dev article of March 2013
Best Web Dev article of August 2012
Best Web Dev article of May 2012
Best Mobile article of January 2012
Best Mobile article of December 2011
Best Mobile article of October 2011
Best Web Dev article of September 2011
Best Web Dev article of August 2011
HTML5 / CSS3 Competition - Second Prize
Best ASP.NET article of June 2011
Best ASP.NET article of May 2011
Best ASP.NET article of April 2011
Best C# article of November 2010
Best overall article of November 2010
Best C# article of October 2010
Best C# article of September 2010
Best overall article of September 2010
Best overall article of February 2010
Best C# article of November 2009

Comments and Discussions

 
GeneralMy vote of 5 PinmemberMike Barthold23-Jul-12 20:57 
GeneralMy vote of 5 Pinmemberconmajia5-Jul-12 5:35 
Questionabout the game help thank you Pinmemberhellosmith28-Feb-12 18:22 
GeneralMy vote of 5 PinmemberYusuf8-Feb-11 10:21 
GeneralRe: My vote of 5 PinmvpMarcelo Ricardo de Oliveira9-Feb-11 7:44 
GeneralHi again from Normandy Pinmemberscalpa9816-Dec-10 7:43 
GeneralRe: Hi again from Normandy PinmemberMarcelo Ricardo de Oliveira16-Dec-10 12:16 
GeneralYou rock man PinmemberKhaniya29-Mar-10 23:01 
GeneralRe: You rock man PinmemberMarcelo Ricardo de Oliveira30-Mar-10 3:03 
GeneralVERY NICE AND CLEVER GAME Pinmemberscalpa9811-Aug-09 0:07 
Hi from Normandy!
It's always amazing (even if i don't understand 10 percents of the code) to see how man can solve this kind of maths game by coding!
I tried to solve a 4X4 puzzle without success... very difficult.
I teach young pupils (10 years old) in primary school and as a hobbyist and with the help of friends coders, I try to build maths games to increase pupils' skills.
I begin to learn C#, and want to build a new game (with the same presentation as theese Labyrinthales or carrés-magiques here : http://www.scalpa.info/new_logiciels.php[^]. Can you help me to find the steps of code, I don't know how and where to begin.
The aim of this simple game is to find all the 4X4 squares hidden inside a big square.
The sum of 4 squares is equal to a specific number. You can have a look here to see what I mean http://scalpa.info/grille.jpg[^]
The variables of the game are as follows:
1. The squares of 4 boxes may or may not overlap.
2. The size of the board. (The large square).
3. The numbers that can be used in the boxes.
4. The target number (100 in the example above).
5. The number of hidden 4X4 squares inside the board.
 
So I would like to built it in C# or VB to print exercises on an A4 paper in the maner of "labyrinthales".
It seems to me i have to follow the architecture of your code : board, cage, cell, point ? No ?
 
Thanks for any help...
 
Pascal, a 50 years old teacher, in a village in Normandy, who tries to learn and not only to teach !
 
scalpa
http://www.scalpa.info

GeneralRe: VERY NICE AND CLEVER GAME PinmemberMarcelo Ricardo de Oliveira11-Aug-09 18:09 
AnswerRe: VERY NICE AND CLEVER GAME Pinmemberscalpa9811-Aug-09 23:07 
GeneralRe: VERY NICE AND CLEVER GAME PinmemberMarcelo Ricardo de Oliveira16-Aug-09 14:04 
AnswerRe: VERY NICE AND CLEVER GAME Pinmemberscalpa9818-Aug-09 11:33 
NewsRe: VERY NICE AND CLEVER GAME Pinmemberscalpa988-Sep-09 10:04 
GeneralAdd compiled app Pinmemberfeanorgem4-Aug-09 10:05 
GeneralRe: Add compiled app PinmemberMarcelo Ricardo de Oliveira4-Aug-09 10:24 
GeneralRe: Add compiled app PinmemberMarcelo Ricardo de Oliveira6-Aug-09 11:18 
GeneralRe: Add compiled app Pinmemberfeanorgem6-Aug-09 11:39 
GeneralRe: Add compiled app Pinmemberfeanorgem21-Aug-09 16:13 
GeneralRe: Add compiled app PinmemberMarcelo Ricardo de Oliveira24-Aug-09 5:06 
QuestionUniquely solvable? Pinmemberdarrellp1-Aug-09 21:17 
AnswerRe: Uniquely solvable? PinmemberMarcelo Ricardo de Oliveira2-Aug-09 5:24 
GeneralRe: Uniquely solvable? PinmemberEric Beijer3-Aug-10 16:20 
GeneralRe: Uniquely solvable? PinmemberMarcelo Ricardo de Oliveira9-Aug-10 4:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 6 Aug 2009
Article Copyright 2009 by Marcelo Ricardo de Oliveira
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid