Learning ProcessingJS, While Sketching Sudoku Puzzle

Sudoku has been sort of "Hello World" for me, whenever I sat down to learn a new computer language or framework. This lead to various versions of "Sudoku Tutor" that I developed using Excel (yes, that Microsoft one!), VBScript, vanilla Javascript, JQuery, Chrome App framework and so on. 

When I drew first Sudoku using Canvas element in HTML, it didn't impress me. The code I had to write was much complicated then any others I had previously written. Then I found ProcessingJS on Khan Academy. It made working with Canvas much simpler and soon I could prepare printable Sudoku, Kakuro, Loop-the-Loop and Hitori puzzles. That was in 2015.

Back to present... I decided to sit again relearning ProcessingJS sans Khan Academy, and develop Sudoku player to play on mobile phone. The result, as of now, is something that you can see in video at bottom where I solved 2 Sudoku, 2 Loops and one each of Kakuro and Hitori in around 35 minutes.

We will start with a plain sketch of Sudoku puzzle for which you would need to download the latest version of ProcessingJS. Put the downloaded file in the same folder as the html file which we would call index.html. Add a plain text file and name it sdk.pde. This would contain the ProcessingJS code

Due to security concerns, external .pde file cannot be called from a local page and thus this entire setup needs to be served using an HTTP server. It is not that big concern as it may sound. You can just add a chrome extension like this one and use it to serve the folder from it.

Add the two lines below to index.html. First line will add the ProcessingJS framework, and second will create a canvas element to the page that will contain the Sudoku sketch. The data-processing-sources attribute will signal ProcessingJS to use sdk.pde for drawing on the canvas. You don't need to specify height or width of canvas as it can be done from ProcessingJS code itself.

  <script src="processing.min.js"> </script>
  <canvas data-processing-sources="sdk.pde"></canvas>

That's all for index.html. Let's edit sdk.pde. The PDE file that effectively contains Javascript code for ProcessingJS processor to understand, will have 2 basic methods setup() and draw().

  void setup() {
  }

  void draw() {
  }

setup() is called only once, whereas draw() is repeatedly called giving you capability to create animations. If you are creating a static sketch it is theoretically possible to write the entire code in setup() to run it only once, but let's use setup() only for code that defines the sketch and write the action code in draw(). The repeated call for draw() can be stopped as you will see below.

void setup() { 
  size(400,400);
  background(#EEEEEE)
  fill(0);
  stroke(0);
  noLoop();
  PFont fontA = loadFont("Calibri");
  textFont(fontA, 40);
  textAlign(CENTER, CENTER);
}

The size() function sets the size of canvas using 2 parameters that are width and height in order. Once the size of canvas is set, these values can be accessed using built-in variables width & height, as we are going to do later.

background() sets the background color whereas fill() and stroke() sets the color to fill in shapes and the lines/borders respectively.

noLoop() tells the ProcessingJS that code within draw() needs to be called only once.

loadFont() loads the font that is passed as parameter into a variable of type PFont which later is passed to textFont() setting the font and fontsize to be used in the canvas. textAlign() specifies the horizontal and vertical alignment of the text.

Having done the canvas setup, let's draw the Sudoku from draw()

  void draw() {
    x = (width - 3) / 9;
    for (ctr1 = 0; ctr1 <= 9; ctr1++) {
      for (ctr2 = 0; ctr2 <= 9; ctr2++) {
        line(ctr2 * x + 1, ctr1 * x, ctr2 * x + 1, (ctr1 + 1) * x);
      }
    }
  }

"width" is the built-in variable that we have called to calculate the size of each cell in variable "x".
The line() takes x and y co-ordinates of the "from" and "to" points and draws a line. Having called it within for-loop we end up with a number of vertical lines as below.


All the lines are showing up with same width, but we need to have thick lines to seperate the 3x3 blocks/regions. strokeWeight() helps to set the line width. Add the below code before line() and see the difference it makes.

        strokeWeight(1);
        if (ctr2 % 3 == 0) { strokeWeight(3); }


Add the three lines below for horizontal lines as well, and we have the complete grid.

        strokeWeight(1);
        if (ctr1 % 3 == 0) { strokeWeight(3); }
        line(ctr2 * x, ctr1 * x + 1, (ctr2 + 1) * x, ctr1 * x + 1);


Now the numbers to be filled in the puzzle. There are 9x9=81 cells of which most are empty and some are filled. Prepare a string concatenating contents of each cell in puzzle, a space representing empty cell. The string would thus be 81-characters long.

Variable "txt" below holds data from Sudoku that appeared in Times of India, Mumbai edition, on 27-June-2019. Split the string into array for easily accessing the numbers.

    txt = "  7  1 2 5   2 916     5 38 6   8  7   3 7   1  6   4 48 5     926 7   1 5 2  3  ";
    a1 = txt.split("");

text() is used to draw the numbers in the cells. Since there are 9 cells each horizontally and vertically whereas loop runs 10 times (for 10 lines), the if condition prevents firing text() 10th  time.

        if (ctr2 != 9 && ctr1 != 9) {
          text(a1[ctr1 * 9 + ctr2], ctr2 * x + x / 2, ctr1 * x + x / 2);
        }

And that finishes the puzzle.


Entire content of draw(), for clarity: 

  void draw() {
    x = (width - 3) / 9;
    txt = "  7  1 2 5   2 916     5 38 6   8  7   3 7   1  6   4 48 5     926 7   1 5 2  3  ";
    a1 = txt.split("");
    for (ctr1 = 0; ctr1 <= 9; ctr1++) {
      for (ctr2 = 0; ctr2 <= 9; ctr2++) {
        strokeWeight(1);
        if (ctr2 % 3 == 0) { strokeWeight(3); }
        line(ctr2 * x + 1, ctr1 * x, ctr2 * x + 1, (ctr1 + 1) * x);
        strokeWeight(1);
        if (ctr1 % 3 == 0) { strokeWeight(3); }
        line(ctr2 * x, ctr1 * x + 1, (ctr2 + 1) * x, ctr1 * x + 1);
        if (ctr2 != 9 && ctr1 != 9) {
          text(a1[ctr1 * 9 + ctr2], ctr2 * x + x / 2, ctr1 * x + x / 2);
        }
      }
    }
  }

And here is the video I mentioned in the beginning.



The player shown in above video uses HammerJS framework (to resize and move puzzle around) in addition to ProcessingJS.

Comments

Popular posts from this blog

The Curious Case of a Girls' School

Down the memory lane