Link to home
Start Free TrialLog in
Avatar of Isaac
IsaacFlag for United States of America

asked on

JavaScript - Chessboard

Hi Gurus,

I'm currently reading a book called "Eloquent JavaScript" and working on all the projects as well.  In Chapter 2, I am stuck on an exercise I've been working on for about a week but I can't figure it out.

Here's the exercise:
Write a program that creates a string that represents an 88 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:
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
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.


Here's my solution but it's just not coming out correctly.  
http://codepen.io/isogunro/pen/dpvKJp?editors=1112

Any hint\help\suggestion\comments would be greatly appreciated.
ASKER CERTIFIED SOLUTION
Avatar of ste5an
ste5an
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Isaac

ASKER

Question 1:
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #

Question 2: (Here''s what my code produced)
" # # # #"
" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"

Question 3:
8x8

Still confused
Avatar of Isaac

ASKER

Actually, my code produced this. Not sure why b/c my loop is 8

" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"
" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"
" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"
" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"
" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"
" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"
" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"
" "
" #"
" # "
" # #"
" # # "
" # # #"
" # # # "
" # # # #"
Avatar of Isaac

ASKER

I think I see the problem...be back. stand by..
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Isaac

ASKER

YES!!!  I got it.  Thanks for the hints gentleman. Now on to the next chapter, Functions.

var num = 8;
for (var i=1; i<=num; i++) {
  var y="";
  for (var x=1; x<=num; x++){
    if(x%2===0){
      y += "#";
    }
    else {
      y += " ";
    }
  }
   console.log(y);
}

Open in new window

Avatar of Isaac

ASKER

Much appreciated
Not exactly, cause:

Hint: How many prints do you think you need?

and

Passing this string to console.

Means you need one "print".. "this string" is singular. Thus you should use one print/output call.
You're currently using num prints.
Using pairs same result. Each row we just alternate what a square looks like ' #' then '# ';
<script>
var num = 8;
var cols = num / 2;
for(var row=1; row<=num;row++) {
  var square = row % 2 ? ' #' : '# ';
  var line = '';
  for(var col=1;col<=cols;col++) {
    line += square;
  }
  console.log(line);
}
</script>

Open in new window

num = 9?
granted
nope, the second is yours:

User generated image
and the bottom, left piece is normally black.
Avatar of Isaac

ASKER

ste5an,

I did use one print (console.log).  Is that what you mean?

What's wrong with num=9?  It produced the correct result.
http://codepen.io/isogunro/pen/dpvKJp?editors=1112
Avatar of Isaac

ASKER

Nevermind...
Coding is a strict process. The requirements tell you that you should create one string and output this single string.

E.g.
var outputCounter = 0;
var num = 9;
for (var i=1; i<=num; i++) {
    var y="";
    for (var x=1; x<=num; x++){
        if(x%2===0){
            y += "#";
        }
        else {
            y += " ";
        }
    }
    console.log(y);
    outputCounter += 1;
}
console.log('Num outputs:' + outputCounter);

Open in new window


And for a 9x9 board the correct output is the first, yours the second:
User generated image
A chess board has an alternate coloring scheme and starts with a black square in the bottom, left corner.
Avatar of Isaac

ASKER

Ahhhh.....I see. dang
A further hint: A "general" chess board is not necessarily squared, thus the task asks for working with independent width and height.
Avatar of Isaac

ASKER

hmmm......looks like I'm not done.  Need to think some more.

BTW Julian, your code produced when I entered it in codepen
"####"
" # # # #"
"####"
" # # # #"
"####"
" # # # #"
"####"
" # # # #"
Just start by building the board row by row.

Hint: sequence of  pieces in a 4x4 board is (ATTENTION, ASCII ART :):
+--+--+--+--+
|16|15|14|13|
+--+--+--+--+
| 9|10|11|12|
+--+--+--+--+
| 8| 7| 6| 5|
+--+--+--+--+
| 1| 2| 3| 4|
+--+--+--+--+ 

Open in new window

As you can rotate a chess board, try to output this:

4|3|2|1|
|5|6|7|8
12|11|10|9|
|13|14|15|16

Open in new window


And before doing this, print this:
|1|2|3|4
|5|6|7|8
|9|10|11|12
|13|14|15|16

Open in new window

Avatar of Isaac

ASKER

This is harder than I thought.
First of all start with the description  of the problem:

[..] using newline characters to separate lines. [..] Passing this string [..]

This translates to pseudo-code using chess semantics:

board =''
foreach rank
        line = 'build line'
        board += line
print board

Then examine how to build a line. Either you can use the idea behind the square sequence or (another approach) use the visual approach: the second rank is the inverse of the first rank.
Avatar of Isaac

ASKER

Why would I need the "\n" if the outer loop creates a new line already?
Well, where you place the add does not really matters. Whether it's alread at the end of the line or you do it in the outer loop, as long as it is added. The point is where do you know that the line is completed?
BTW Julian, your code produced when I entered it in codepen
Are you sure you entered it incorrectly - here is a link to the code - check the output in the console.
The link return a blank page in Edge..
This might be jumping the gun on your learning path but there are some other techniques that can be used to solve this problem. The following is a discussion in thinking about the problem and how we can use what we know about the problem to derive a solution.

Lets look at what you are trying to achieve.
There are two possible output values a space ' ' and hash '#'
You are basically alternating between those.

The second consideration is that each odd row starts with the same character and each even row starts with the same character.

Rows can be odd or even. When even, the last character on the current row is the same as the starting character on the following row. But with odd rows the characters alternate across rows.

Example 2x2
| #|
|# |

Open in new window

If we flatten the above out we get
| ## |

Open in new window

Example 3x3
| # |
|# #|
| # |

Open in new window

If we flatten this one out we get
| # # # # |

Open in new window


The third consideration is the last row should always end in a space (if we follow the rule that a chessboard always has a white square in the bottom right hand corner)

Consider the grids above. Both of them satisfy this consideration. What do we notice about the first row in each case? In both cases the row starts with a space.
 
How can we use the information in the above considerations to solve the problem.
Instead of using if statements why don't we rather store the output characters in an array like so
var square=['#',' '];

Open in new window

We need an index into this array - lets call it k.

We need k to alternate between the two squares on each output within a row i.e. k needs to flip between 1 and 0.
There is a neat trick to getting a variable to flip between 1 and 0
k = 1 - k

Open in new window

When k is 1 the result is 0
When k is 0 the result is 1
Using the above we can put the following together
var num = 8;
var square=['#', ' '];
for(var row=1; row<=num;row++) {
  line = '';
  for(var col=1; col<=num;col++) {
    line += square[k];
    k = 1 - k; 
  }
  console.log(line);
}

Open in new window

This code is not quite ready yet. Firstly, we have not initialised k to a starting value. If we look at our discovery in the third consideration above we see that the first row must always start with a ' ' which means, given our square array k needs to start as a 1.
We could therefore do this
var num = 8;
var square=['#', ' '];
var k = 1;
for(var row=1; row<=num;row++) {
  line = '';
  for(var col=1; col<=num;col++) {
    line += square[k];
    k = 1 - k; 
  }
  console.log(line);
}

Open in new window

This is also still not quite there - if we run it for num = odd number it works - but for even numbers it just prints out N of the same row. This is because of what we found in the second consideration i.e. N = odd number characters alternate across rows, N = even number the last character of the current row is the same as the first character of the next. This means that we need to initialise k differently on each row. This is easily done. We already have the outer loop that is alternating between odd and even numbers - we can use that to initialise k. Whatever row N starts with row N + 1 must be the other character.
We know the first row must start with a space, and we know that the space is index 1 in the square array so k must start at 1. In other words when
row = 1, k = 1
row = 2, k = 0
row = 3, k = 1
...

Open in new window

If you study the pattern of the above you will see it follows row modulus 2
To complete our code we can therefore put an initialiser for k on each iteration of the outer loop like this
var num = 8;
var square=['#', ' '];
for(var row=1; row<=num;row++) {
  line = '';
  k = row % 2;
  for(var col=1; col<=num;col++) {
    line += square[k];
    k = 1 - k; 
  }
  console.log(line);
}

Open in new window

The link return a blank page in Edge..
As it should - it is pure JavaScript to see the output you need to F12 and for Edge you need to refresh the page when the console is open. console is not available when the console window is not open.
If we want to stick to the exact requirements of the problem which is to separate lines with \n chars and have one output statement then we need to
a) Initialise line outside the loop
b) Remove the re-initialisation of the line in the loop
c) Terminate line with a \n at the completion of each inner loop
d) Move the console.log(line) outside both loops.
<script>
var num = 8;
var square=['#', ' '];

// a) Initialise line outside the loop
var line = '';
for(var row=1; row<=num;row++) {

  // b) Remove the re-initialisation of the line in the loop
  //  line = '';

  k = row % 2;
  for(var col=1; col<=num;col++) {
    line += square[k];
    k = 1 - k; 
  }

  // c) Terminate line with a \n at the completion of each inner loop
  line += '\n';
}

// d) Move the console.log(line) outside both loops.
console.log(line);
</script>

Open in new window

using the square sequence
var height = 8;
var width = 10;
var board = '';
for (var rank = 1; rank <= height; rank++) {
    var line = '';
    for (var file = 1; file <= width; file++ ) {
        line = ((file + (rank % 2 === 0 ? 0 : 1)) % 2 === 0 ? '#' : '.') + line;
    }
    board += line + '\n';
}
console.log(board);

Open in new window

Avatar of Isaac

ASKER

Thanks Julian!

I need to dissect your scripts in chrome, especially the last one.  The "\n" is throwing me off.  I would think that the outerloop would cause the newline implicitly.
I would think that the outerloop would cause the newline implicitly.
If you are console.log()'ing on each iteration of the outer loop it would - but we are only doing the console.log() after all iterations of both loops are complete i.e. just 1 single console.log() of a long line with all the rows. Therefore the \n is needed to break those lines up.

The two samples show the two different approaches - the first does N console.log() output (1 for each row - so no \n is required) the second as a single output - which needs \n

The original question stated
Write a program that creates a string that represents an 8x8 grid, using newline characters to separate lines
I also interpret the question to mean a square board rather than one allowing for a different height and width. They ask for a variable called size - not width and height, so diverging into code on how to handle non-square boards might be clouding the issue.
Avatar of Isaac

ASKER

Ahh...Ok.  I see now.
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.
Does imho not impose a constraint on width and height.. otherwise replace height and width by size.
Does imho not impose a constraint on width and height.. otherwise replace height and width by size.
It is ambiguous but I put it down to bad phrasing. If they wanted variable width and height they would have probably said
"define variables width and height ..."
But they said size - one variable for me implies a square. Given the level of this teaching example and what it is trying to achieve I would not think that a non-square output would not be a requirement for this question - maybe a follow up question. This seems to be teaching about how to obtain a specific output (8x8) and then change to the generic (size x size)
Having said that - I would solve it like this - building on the above example
First we would need to define a row and col value to replace num.
We would need to replace the loop terminators with these values.
Finally, we need to change how we determine the start value for k in the outer loop.
Once that is correct the rest will follow.
There are 4 possible scenarios we need to look at
Rows: EVEN
Cols: EVEN
| x|
|x |

Open in new window

Rows: EVEN
Cols: ODD
|x_x|
| x |

Open in new window

Rows: ODD
Cols: EVEN
|x |
| x|
|x |

Open in new window

Rows: ODD
Cols:ODD
| x |
|x x|
| x |

Open in new window

To keep true to the white square bottom right we need a truth table of what character to start on for each of the above
ROWS COLS  :  RESULT
EVEN EVEN  :    1
EVEN ODD   :    0
ODD  EVEN  :    0
ODD  ODD   :    1

Open in new window

That looks like an XOR result
So what if we set start to the following
var start = (rows % 2 ^ cols % 2);

Open in new window

That will give us the opposite of what we want i.e
ROWS COLS  :  RESULT
EVEN EVEN  :    0
EVEN ODD   :    1
ODD  EVEN  :    1
ODD  ODD   :    0

Open in new window

But this is ok because we can use the same trick we are using to flip k to flip start and we can start off by setting k to start flipped which will give us the required result.
In other words in the outer loop we have
   k = 1 - start;
   start = k;

Open in new window

Because the XOR initialistion of start resulted in a 0 for an EVEN / EVEN pairing the result for the initialisation of k will be 1. After that the flip will ensure we stay on track.
Putting it all together
<script>
// REPLACE num WITH totalrows AND totalcols
// PREFIX WITH total SO AS NOT TO CONFUSE
// WITH LOOP row / col
var totalrows = 3;
var totalcols = 3;
var square=['#', ' '];
var line = '';

// INTIALISE START WITH XOR OF MOD's
var start = (totalrows % 2 ^ totalcols % 2);
for(var row=1; row<=totalrows;row++) {
  // INITIALISE k WITH INVERSE OF START
  k = 1 - start;

  // SET start FOR NEXT ITERATION
  start = k;

  // THE REST REMAINS THE SAME
  for(var col=1; col<=totalcols;col++) {
    line += square[k];
    k = 1 - k; 
  }
  line += '\n';
}
console.log(line);
</script>

Open in new window