Eloquent JS, Chp2: Exercise 3

Exercise 3: Chess Board

Write a program that creates a string that represents an 8 x 8 grid, using newline characters to separate lines.

At each position of the grid there is either a space or a “#” character.

The characters should form a chess board.  Passing this string to console.log should show something like this:

chess board

When you have a program that generates this pattern, define a variable size = 8 and change the program so that it works for any size, outputting a grid of the given width and height.

Approach

chess board 8 by 8 with arrows

Loop – “Vertical”:

  • This will be the outermost for loop
  • This loop adds height to the output
  • Let i represent the number of iterations
  • i is of type number
  • Let size represent the width and height of the square
  • size is of type number
  • By the end of the loop, i == size

Loop – “Horizontal”:

-Increases characters horizontally per line
-Each new line is a new iteration
-Repeating characters: # and a space
-When i is an even number, the line starts with a # character
– Repeated characters when i is an even number: #_
-When i is an odd number, the line starts with a space (will rep. with _ character in notes)  -Repeated characters when i is an odd number: #_ , but one _ exists before any repeats

  • This will be an inner for loop
  • This loop adds width to the output
  • Let char represent the characters on the line
  • char is of type string
  • char.length returns the length (number of characters) of char
  • By the end of the loop, i == size == char.length

Questions:

1. How do you repeat string characters horizontally when the number of repeats required is unknown? 

You can concatenate strings i.e.

console.log("# " + "# ")
//-> # #

EDIT (after writing Solution Attempt 4 – Final Submission) : You CAN repeat strings horizontally when the number of repeats is unknown.

What is needed: Put console.log outside the for loop (so whatever is printed is the final result of repeated string concatenation).  See the next blog post “Exercise 2.3 Part 2” for this solution. 

But recall that no arithmetic operation can be applied to strings i.e.

var test = "Hi";
test *= 2;
console.log(test); 
//-> NaN

 

I will not be able to repeat characters horizontally using arithmetic multiplication i.e.

var char = "# " * 3; 
console.log(char);
//-> NaN

or

console.log("10" * 2); 
//-> 20 
console.log(typeof("10" * 2));
//-> number

 

Observe string concatenation between string values and number values:

//Example 1
console.log("150" + 1010);
//-> 1501010 
console.log(typeof("150" + 1010));
//-> string  

//Example 2
console.log(150 + "1010");
//-> 1501010
console.log(typeof(150 + "1010")); 
//-> string 

//Example 3
console.log("#" + 10);
//-> #10
console.log(typeof("#" + 10));
//-> string

Keep in mind that the Number and String functions convert a value to type number and type string respectively.

Though that still isn’t helpful in repeating string characters horizontally when the number of repeats required is unknown.

It passed the mind to replace # and space with 1 and 0, and at the end, replace 1 and 0 with # and space again. Using numbers would allow for repetition via arithmetic operations. However, this idea died a swift death upon the realisation that when a string value’s type is converted to type number, (“10” with 10), any arithmetic operation on this value will be governed by mathematical laws i.e. 10 * 2 = 20, not 1010 i.e.

ejs 2.3 repeat test with 10

And why it wouldn’t work with # :

ejs 2.3 repeat test

.
.
.

2. What is the update/final-expression in the inner for loop? 

Can it be empty?

.
.
.

Outside the scope of Chapter 2, there is a JavaScript String repeat() method. Let’s try this out for Solution 1.

// Allow any size to be inputted
 var size = prompt("Enter a positive integer.");

// Convert string value inputted in prompt to type number
 Number(size);

// Outer loop to track vertical iteration
 for (var i = 0; i <= size; i++) {

// Inner loop to add characters horizontally per line
 for(var char = ""; char.length <= size; ) {

//If the line number is odd
 if (size % 2 !== 0) {

// Begin the line with a space
 char = " ";
 // How many times does the pattern have to repeat?
 var numRepeats1 = (((size + 1)/2) - 1);
 var pattern1 =  "# ";

// Concatenate the first space with the pattern
 // Use string repeat() method to repeat the pattern the appropriate number of times
 char += pattern1.repeat(numRepeats1);

}

//If the line number is even
 if (size % 2 == 0) {

//Pattern to be repeated
 var pattern2 = "# ";

// Repeat the pattern this amount of times
 var numRepeats2 = size/2;

//Update char value
 char += pattern2.repeat(numRepeats2);
 }

.
.
.

EPIPHANY: I don’t need the second for loop. With the string repeat method, there is no need for a loop to repeat the characters horizontally.

EPIPHANY 2 (after Solution Attempt 1 Test 1): Nevermind, I do need the second loop. Without this loop, a conditional branch would be left out depending on what the value of size is i.e. if size = 2, the odd number if-statement branch is not considered at all, so instead of printing out line 1, line 2, only line 2 with weird stuff is printed.

Solution Attempt 1

var char = ""
var size = prompt("Enter a positive integer.");
Number(size); 

for (var i = 0; i <= size; i++) {
    if (size % 2 !== 0) {
       char = " "; 
       var pattern1 = "# "; 
       var numRepeats1 = (((size + 1)/2) - 1);
       char += pattern1.repeat(numRepeats1); 
       console.log(char);
}

    if (size % 2 == 0) { 
       var pattern2 = "# "; 
       var numRepeats2 = size/2; 
       char += pattern2.repeat(numRepeats2); 
       console.log(char); 
}}

 

chessboard repeatmethod soln1
size = 2

No plan survives first contact with the enemy. I think I’ll become quite fond of that saying.

What I think went wrong:

Firstly, the initial value of i should be set to 1, not zero. This way, the iteration number is equal to the row number (of the output).

ejs 2.3 s1t1 iequals1
size = 2; change is highlighted

 

Secondly, the if-statement conditions are wrong. Variable i instead of variable size should be used. That is:

// for odd-number lines 
 if (i % 2 !== 0) {
  }
// for even-number lines 
  if (i % 2 == 0) {
  }

Why? Because it is the iteration value that tracks the row that we are on. The value of i increases with each iteration, while the value of size stays the same (it is whatever value was inputted in prompt). Size is the maximum width and height of the grid. We just need the current row number of the grid (i).

Let’s say size = 2. The odd-numbered line if-statement branch {check wording} is evaluated to be false since size is an even-numbered value. That’s why char = ” ” didn’t manifest. Execution moves to the even-numbered line if-statement branch, and its statement body is executed.

NB: The value of i is compared to the value of size at the beginning of each iteration to see if another iteration is required. {check wording and understanding: is the updated value compared with the value in the condition at the END or the BEGINNING of each ITERATION (or do we say LOOP)?}

ejs 2.3 s1t2
size = 2; changes are highlighted

What happened?

Look at numRepeats1 and numRepeats2. Are their formulas correct? Seems to be so, yes. Next, look at char +=.

ejs 2.3 s1t4 char +=
size = 2; change is highlighted

QUESTION 1: Why did a change from Line 17 char += pattern2.repeat(numRepeats2); to char = pattern2.repeat(numRepeats2); give the correct output for the 2nd line?

Testing

1)  Line 17 –>  char += re-added
Line 7 –> char = “1”
Changes are to see whether the new value of char bleeds out of its if-statement into the rest of the program. Apparently yes. char = “1” should only affect odd number iterations.

2.3 ejs repeat test char +=
size = 2

ANSWER to QUESTION 1: For some reason, the value of char remains “1# # # … #” even when the iteration is a new, even-numbered iteration. That the iteration is even-numbered means that execution should bypass the first if statement (Line 6) and go on to the second if statement (Line 14). char’s value should be an empty string when execution starts at Line 14, not holding on to whatever value it gained from Line 10.

We must also consider the += operator on Line 17. What happens when it is removed?

2) Line 17 –> = operator replaces += operator

2.3 ejs repeat test char += 2
size = 2

ANSWER to QUESTION 1: When char (whose value seems to cling to the result from Line 10) is not concatenated to pattern2 on Line 17, the output for iteration number 2 looks proper.

 

 

9 repeats
size = 2

if-statement for even-number lines seem to be working properly. Something’s wrong with the if-statement for odd-number lines branch. Suspect are Lines 9 and/or 10.

 

Looking at Line 9 — changed (((size + 1)/2) – 1); to (size):

it's the repeat formula
Line 9, testing the repeat formula

 

Testing the repeat formula:

repeat testing

That means something is wrong with char += or size input, not the formula.

EDIT: At least, for when size holds ODD NUMBERS. How about EVEN NUMBERS?

odd formula doesn't work with even numbers

EPIPHANY: Whenever size is an odd number, the repeating formula in the first if-statement works fine. But the repeating formula in the second if-statement doesn’t work properly. AND VICE VERSA. All because of variable size, which is used in the repeating formula. FIX THE FORMULAS.

 

Repeating a string 2.5 times doesn’t happen. It repeats 2 times.

even and odd repeating formula testing

But the if-statement for odd-number and the if-statement for even numbers should filter incoming numbers, and slide them into their respective formulas.

 

 

The String repeat() method and char += … seems to be working fine.

what's wrong is the loop or lack of one

 

 

 

Testing prompt input:

it's the prompt input that's the problem omg
size = 3 ; highlighted is the output you’re supposed to have gotten

 

Something’s wrong with the prompt input.

More proof:

2.3 ejs no prompt9 repeats

 

By process of elimination, these are the remaining potential errors:

  • Repeating formulas — fix both the odd and even one; use something other than size or adjust the numbers to accommodate variable size
  • REPEATING PATTERN ISSUE and discrepancy between value of i and value of size
  • Don’t cheat with using blanks as noncharacters. Spaces count as characters and you want to print the specified character amount, no more no less.
  •  Prompt input
  • for loop? lack of a secondary loop?

 

 

THE BIG EPIPHANY

When size holds an odd number value, the numRepeats1 formula works properly. But the numRepeats2 formula does not. When size holds an even number value, the numRepeats2 formula works properly, but the numRepeats1 formula does not.

chessboard big epiphany

The solution: 

if size = odd number { 
   if i = odd number 
     Adjust repeating formula accordingly
   if i = even number 
     Adjust repeating formula accordingly 
}
else if size = even number {
   if i = odd number
     Adjust repeating formula accordingly
   if i = even number
     Adjust repeating formula accordingly 
}

Place these if statements after the for loop. Each if statement will have a nested if statement (for i = odd number) and else-if statement (for i = even number).

Solution Attempt 2

var char = "";
var size = 2;
Number(size);

for (var i = 1; i <= size; i++) {
  // if size is an odd number
  if (size % 2 !== 0) {
    //if the row number (iteration) is an odd number 
    if (i % 2 !== 0) {
      char = " ";
      var pattern1 = "# ";
      var numRepeats1 = (((size + 1)/2) - 1);
      char += pattern1.repeat(numRepeats1);
      console.log(char);
    }
    //if the row number is an even number 
    if (i % 2 == 0) {
      char = "#"
      var pattern2 = " #";
      var numRepeats2 = ((size -1)/2);
      char += pattern2.repeat(numRepeats2);
      console.log(char);
}}
  //If size is an even number 
  else if (size % 2 == 0) {
    //if the row number is odd
    if (i % 2 !== 0) {
      var pattern1 = " #";
      var numRepeats1 = (size/2);
      char += pattern1.repeat(numRepeats1);
      console.log(char);
     }
  //if the row number is even 
  if (i % 2 == 0) {
     var pattern2 = "# ";
     var numRepeats2 = (size/2);
     char += pattern2.repeat(numRepeats2);
     console.log(char);
 }}}

^Makes more structural sense for the currently outermost for loop to be inside “if size is odd” and “if size is even” statements. Is the structure causing the following problem though? This structure, while disorganized, should still work properly.
** “if size is odd” if statement branch is working properly. Something’s wrong with the “if size is even” statement

2.3 ejs size is even if statements
size = 2

 

Get rid of += on Line 33:
Highlighted yellow is the change:

2.3 ejs size is even if statements rid of +=
size = 2

Why this change when += was replaced by = , on Line 33?

 

Now there are issues with the += on Line 26:

2.3 why why why

The pattern marked “2” is the pattern on Line 24, repeated twice. But where did the pattern marked “1” come from?

char += pattern1.repeat(numRepeats1); 
//equivalent to:
char = char + pattern1.repeat(numRepeats1);

char should be an empty string, until it is concatenated with pattern1.

Highlighted yellow is the change from += to + on Line 26:

2.3 what is char in the else if statement

So char = “# # ” when execution hit upon Line 26? QUESTION 2: How?

 

Using other values for size i.e. 3, 5, 6, 8, 10, the program now displays the proper output. On Line 2, I re-added prompt(), which also works fine now.

EDIT (during Solution Attempt 3): Oh no, prompt() doesn’t work fine for odd numbers.

 

Solution Attempt 3 

1     var char = "";
2     var size = prompt("Enter a positive integer.");
3     Number(size);
4
5     for (var i = 1; i <= size; i++) {
6         if (size % 2 !== 0) {
7             if (i % 2 !== 0) {
8                 char = " ";
9                 var pattern1 = "# ";
10                var numRepeats1 = (((size + 1)/2) - 1);
11                char += pattern1.repeat(numRepeats1);
12                console.log(char);
13            }
14
15            if (i % 2 == 0) {
16                char = "#";
17                var pattern2 = " #";
18                var numRepeats2 = ((size -1)/2);
19                char += pattern2.repeat(numRepeats2);
20                console.log(char);
21    }}
22        else if (size % 2 == 0) {
23            if (i % 2 !== 0) {
24                var pattern1 = " #";
25                var numRepeats1 = (size/2);
26                char = pattern1.repeat(numRepeats1);
27                console.log(char);
28            }
29
30            if (i % 2 == 0) {
31               var pattern2 = "# ";
32               var numRepeats2 = (size/2);
33               char = pattern2.repeat(numRepeats2);
34               console.log(char);
35    }}}

I celebrated too soon.

When size = 11 without prompt(), we get this:

i celebrated too soon 2.3 ejs
Output is proper as expected

 

When size = 11 via prompt(), we get this mess:

wtf 2.3 ejs prompt

Inputting size values into prompt() that are even and greater than 10 i.e. 12, 14, 16, etc., the output is proper as expected. However, when inputting size values into prompt() that are ODD and greater than 10 i.e. 11, 13, 15, 17, etc., the output is all wrong.

EDIT: Nope, odd-numbers below 10, when inputted via prompt() for size, also do not work properly.

Therefore, we’re dealing with either an error(s) with 1)  Lines 5 – Line 18 (when size is an odd number) or 2) the prompt() method or 3) both or 4) something else.

Note that the even-numbered rows do show the expected output. The odd-numbered rows show more than the expected characters. So the error is somewhere in Lines 6-11 if we’re looking at option 1).

Suspects:

  • Indentation? Line 13 if statement is indented more than it should be? This is unlikely to be the problem. (Check: Yeah, it isn’t the issue.)
  • prompt()       The main suspect
  • The += operator on Line 10  (Check: Not the issue.)
  • usage of same variable for different values i.e. char (Line 7, Line 10) ?? (Check:Not an issue.)

Tentative conclusion: It looks like a prompt() issue, because inputting any value manually gives the correct output. RETURN TO THIS.

 

Cleaning up the code:

see Solution Attempt 4

  • defined the variable char, but did not initialize it –> its value is undefined; will give char a value depending on the nested if statement
  • declared and initialized variables patternsharp and patternspace outside of the for loop –> prevents re-writing these four times inside the loop’s nested if and else if statement bodies
  • renamed numRepeats1 and numRepeats2 –> under one variable name: numRepeats (see Line 9), because they are contained within their nested if statements
  • under the else-if statement on Line 19, pulled two numRepeats out of their nested if-statements, and placed it as one numRepeats statement (Line 20) –> this applied to both nest if-statements and saves space (no need to rewrite numRepeats twice since they have the same formula of size/2)

 

Solution Attempt 4: Final Submission

using String repeat() method

still having issues with prompt()

2.3 solution 1 final

Solution Attempt 4 with Comments

var char; var patternsharp = "# "; var patternspace = " #"; 
var size = 3;
Number(size);

    for (var i = 1; i <= size; i++) {
         // size is an odd number
         if (size % 2 !== 0) {
             // row number is an odd number
             if (i % 2 !== 0) { 
                //odd rows start with a space
                char = " "; 
                // the number of times patternsharp shoud be repeated
                var numRepeats = (((size + 1)/2) - 1);
                /*concatenating " " and patternsharp however 
                many times it's repeated*/ 
                char += patternsharp.repeat(numRepeats); 
                console.log(char); 
             }
            //row number is an even number
            if (i % 2 == 0) {
               //even rows start with a sharp
               char = "#";
               //the number of times patternspace should be repeated
               numRepeats = ((size-1)/2);
               char += patternspace.repeat(numRepeats);
               console.log(char);
 }}
        //size is an even number 
        else if (size % 2 == 0) {
             /*the number of times patternsharp or patternspace 
             should be repeated */
             numRepeats = (size/2);
            //row number is odd
            if (i % 2 !== 0) {
                char = patternspace.repeat(numRepeats); 
                console.log(char); 
        }
            //row number is even
            if (i % 2 == 0) {
               char = patternsharp.repeat(numRepeats);
               console.log(char);
 }}}

 

QUESTION 3: 

I defined and initialized the variable numRepeats on Line 9. The later usage of numRepeats on Lines 15, 21, and 27 do no require being defined again, even when they are inside different if/else-if statements. QUESTION: Was that defining a variable ‘locally’? Versus ‘globally’ before the for loop?

var numRepeats inside a nested if statement
still works properly

 

QUESTION 4: 

What happens when you take out the console.log(char); from the if statement body, and place it outside of it? See Line 13.

console 2.3 ejs

 

 

For the Future

  • I didn’t try a solution without String repeat() method. Try that.
  • Answer Questions 1-4 properly.
  • Figure out what is wrong with prompt() or Lines 6-11.
  • Consider other methods to solve Chess Board — replace(), a second for loop, newline character, etc.
  • Then check your solutions with the Book Solutions.

TBC