Click here to Skip to main content
15,867,330 members
Articles / Desktop Programming / Windows Forms

Calcoolation: A Math Puzzle Board Game

Rate me:
Please Sign up or sign in to vote.
5.00/5 (23 votes)
6 Aug 2009CPOL5 min read 119.4K   2.9K   53   36
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:

C#
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:

C#
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.

C#
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)


Written By
Instructor / Trainer Alura Cursos Online
Brazil Brazil

Comments and Discussions

 
GeneralWorks For Only Two Pairs Pin
Member 1331908920-Jul-17 20:25
Member 1331908920-Jul-17 20:25 
GeneralMy vote of 5 Pin
Mike (Prof. Chuck)23-Jul-12 20:57
professionalMike (Prof. Chuck)23-Jul-12 20:57 
GeneralMy vote of 5 Pin
conmajia5-Jul-12 5:35
conmajia5-Jul-12 5:35 
Questionabout the game help thank you Pin
hellosmith28-Feb-12 18:22
hellosmith28-Feb-12 18:22 
GeneralMy vote of 5 Pin
Yusuf8-Feb-11 10:21
Yusuf8-Feb-11 10:21 
GeneralRe: My vote of 5 Pin
Marcelo Ricardo de Oliveira9-Feb-11 7:44
mvaMarcelo Ricardo de Oliveira9-Feb-11 7:44 
GeneralHi again from Normandy Pin
scalpa9816-Dec-10 7:43
scalpa9816-Dec-10 7:43 
GeneralRe: Hi again from Normandy Pin
Marcelo Ricardo de Oliveira16-Dec-10 12:16
mvaMarcelo Ricardo de Oliveira16-Dec-10 12:16 
GeneralYou rock man Pin
Khaniya29-Mar-10 23:01
professionalKhaniya29-Mar-10 23:01 
GeneralRe: You rock man Pin
Marcelo Ricardo de Oliveira30-Mar-10 3:03
mvaMarcelo Ricardo de Oliveira30-Mar-10 3:03 
GeneralVERY NICE AND CLEVER GAME Pin
scalpa9811-Aug-09 0:07
scalpa9811-Aug-09 0:07 
GeneralRe: VERY NICE AND CLEVER GAME Pin
Marcelo Ricardo de Oliveira11-Aug-09 18:09
mvaMarcelo Ricardo de Oliveira11-Aug-09 18:09 
Hi Pa-scal (or scal-pa Smile | :) )

I'm very excited to hear about your work with children, I wish I had a teacher like that! Too bad I didn't have teachers who knew how to teach math in a fun way Smile | :)

I've just downloaded "Labyrinthales" and "Carrés Magiques", and hey, what a great work, beautiful interface there! Very handy for creating printable games

As for the new game you want to build, I'm thinking in a way to render the table based on the variables you need. I don't know if there's a specific order for columns or rows, or if it's possible to repeat the numbers, I think I didn't get that. Anyway, I'll try to get a sample of code tomorrow.

see you soon!
marcelo
AnswerRe: VERY NICE AND CLEVER GAME Pin
scalpa9811-Aug-09 23:07
scalpa9811-Aug-09 23:07 
GeneralRe: VERY NICE AND CLEVER GAME Pin
Marcelo Ricardo de Oliveira16-Aug-09 14:04
mvaMarcelo Ricardo de Oliveira16-Aug-09 14:04 
AnswerRe: VERY NICE AND CLEVER GAME Pin
scalpa9818-Aug-09 11:33
scalpa9818-Aug-09 11:33 
NewsRe: VERY NICE AND CLEVER GAME Pin
scalpa988-Sep-09 10:04
scalpa988-Sep-09 10:04 
GeneralAdd compiled app Pin
feanorgem4-Aug-09 10:05
feanorgem4-Aug-09 10:05 
GeneralRe: Add compiled app Pin
Marcelo Ricardo de Oliveira4-Aug-09 10:24
mvaMarcelo Ricardo de Oliveira4-Aug-09 10:24 
GeneralRe: Add compiled app Pin
Marcelo Ricardo de Oliveira6-Aug-09 11:18
mvaMarcelo Ricardo de Oliveira6-Aug-09 11:18 
GeneralRe: Add compiled app Pin
feanorgem6-Aug-09 11:39
feanorgem6-Aug-09 11:39 
GeneralRe: Add compiled app Pin
feanorgem21-Aug-09 16:13
feanorgem21-Aug-09 16:13 
GeneralRe: Add compiled app Pin
Marcelo Ricardo de Oliveira24-Aug-09 5:06
mvaMarcelo Ricardo de Oliveira24-Aug-09 5:06 
QuestionUniquely solvable? Pin
darrellp1-Aug-09 21:17
darrellp1-Aug-09 21:17 
AnswerRe: Uniquely solvable? Pin
Marcelo Ricardo de Oliveira2-Aug-09 5:24
mvaMarcelo Ricardo de Oliveira2-Aug-09 5:24 
GeneralRe: Uniquely solvable? Pin
Eric Beijer3-Aug-10 16:20
Eric Beijer3-Aug-10 16:20 

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

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