Assignment 13

Task

Write a program that prints a number of concentric squares of user-specified size to the screen.

Concepts: nested loops and conditionals.
Textbook: Chapter 5 (review)

Steps

Ask the user to enter an odd integer between 1 and 79. (If the user does not, you do not need to keep prompting them; you may just gracefully exit the program with an error message.)

Then, draw concentric squares that are alternately comprised of '#' and ' ' characters. (See the sample output below for examples.) The outer square should always be of #s and have a width and height equal to the number given by the user. This means the center character may be a ' ' or '#' depending on the size of the square. Use one or more loops to draw the square(s).

Sample Output

D:\TA\grading\A13>java ZtomaszeA13
Enter the size of a square to draw (an odd number between 1 and 79): 10
Size must be an odd integer between 1 and 79.
Please try again.

D:\TA\grading\A13>java ZtomaszeA13
Enter the size of a square to draw (an odd number between 1 and 79): 1
#

D:\TA\grading\A13>java ZtomaszeA13
Enter the size of a square to draw (an odd number between 1 and 79): 3
###
# #
###

D:\TA\grading\A13>java ZtomaszeA13
Enter the size of a square to draw (an odd number between 1 and 79): 5
#####
#   #
# # #
#   #
#####

D:\TA\grading\A13>java ZtomaszeA13
Enter the size of a square to draw (an odd number between 1 and 79): 7
#######
#     #
# ### #
# # # #
# ### #
#     #
#######

D:\TA\grading\A13>java ZtomaszeA13
Enter the size of a square to draw (an odd number between 1 and 79): 29
#############################
#                           #
# ######################### #
# #                       # #
# # ##################### # #
# # #                   # # #
# # # ################# # # #
# # # #               # # # #
# # # # ############# # # # #
# # # # #           # # # # #
# # # # # ######### # # # # #
# # # # # #       # # # # # #
# # # # # # ##### # # # # # #
# # # # # # #   # # # # # # #
# # # # # # # # # # # # # # #
# # # # # # #   # # # # # # #
# # # # # # ##### # # # # # #
# # # # # #       # # # # # #
# # # # # ######### # # # # #
# # # # #           # # # # #
# # # # ############# # # # #
# # # #               # # # #
# # # ################# # # #
# # #                   # # #
# # ##################### # #
# #                       # #
# ######################### #
#                           #
#############################

What to Submit

Upload your UsernameA13.java file to Tamarin.

Grading [6 points]

1 - Compiles
Your program compiles successfully (no errors) and follows coding standards.
1 - Input
Reads in the size from the user; prints an error message if it is not an odd integer between 1 and 79 (inclusive).
4 - Prints square
The outer-most square must be of #s with a width and height equal to the user-given value. Interior of shape should be composed of alternating squares made of # and space characters. Shape should be drawn using one or more loops (-2 if not).

FAQs

Problem set from lab
Section 001 & 003: Practice for students (individually or in small groups). Use one or more loops to do the following:
  1. Print all the positive integers less than 100 that are divisible by 3. (1B: Once you have then done, then print those numbers 5 per line.)

Given a user-specified width:

  1. Print a line of "width" asterisks (*). That is, if the user enters 3, print 3 asterisks.
  2. Print a square box of *s with the given width (and height) That is, if the user enters 3, print:
     ***
     ***
     ***
    
  3. Print a box with a hole in its center. That is, print a box as in #3 but with the single * in the center (or a * close to the center for boxes with an even width) replaced with a space.
  4. Print an equilateral triangle of *s with the given width. The right angle should be on the lower left. So, given a width of 4:
     *
     **
     ***
     ****
    
  5. Print an equilateral triangle of *s with the given width. The right angle should be on the lower right. So, given a width of 4:
        *
       **
      ***
     ****
    
Challenge:
Wow, this is hard! How do I do this?
The hardest part of this assignment is figuring out the algorithm. Writing the code for this one took me about 5 minutes. But that was only after I spent about 30 minutes with a piece of paper and a pencil exploring the problem. That is an important lesson: I spent 6 times longer thinking about the problem than I did coding it. (And then it took me a couple hours to write up all I learned, with diagrams, in this FAQ.)

To help you out with this part, here's what I figured out, as well as the process that got me there.

Step 1: First, I started with a sample square large enough to notice the patterns involved (size = 7):

Sample output

Now, as you hopefully learned from the sample problems above, when printing squares of text, it is usually useful to use two nested for loops: one for x and one for y. Character (0,0) would be in the upper left corner, with x increasing to the right and y increasing the farther down you go.

However, in this situation, it seemed easier to me to think about the problem if the center character was position (0,0). So the first problem is how to print the square using the following coordinate system:

Coordinates

Remember this needs to work with any odd-numbered size, so we need to figure out the relationship here so we can phrase everything in terms of size. We are going to print characters in a left-to-right and top-down order. So, instead of x and y (our loop counters) going from 0 to 6 (< size), we want them each to go from -3 to 3. So how do we get -3 and 3 from size (which is 7 here)? This seems to be simply:
  int halfSize = size / 2;
(Finally a chance to use int math intentionally!) Then we can simply iterate both x and y from -halfSize to +halfSize. If you check this with squares of different sizes--like 1, 5, 9, 11--you should find that this relationship holds. (Their halfSizes would be: 0, 2, 4, 5.)


Step 2: Okay, so this lets us print the characters in terms of this coordinate system, but we still need to determine which character to print at each (x,y) character position.

Attempt 1: At first, this seemed to me to be a simple alternating pattern: if x or y is odd, print a #; otherwise, print a space. But this rule doesn't quite work: it would incorrectly print a # at positions like (-2, -1) and (-1, -2).

Attempt 2: Instead, it seems more like the distance from the center would be a better measure: if the distance is odd, print a #; if even, print a space. But this doesn't work once you get aways out from the center. For example: (-3, -3)'s distance = √((-3 * -3) + (-3 * -3)) = √18 = 4 (as an int) = even = space. But that character should be a #. So that didn't work either.

Solution: Instead, we want to simply count how many squares we are from the center, like this:

Distance from center

How do we get these numbers? It's basically the absolute value of the bigger of the x and y for that point. In other words: d = max(|x|, |y|). Or, as code:
  int distance = Math.max(Math.abs(x), Math.abs(y));


Step 3: Now, we're not done quite yet. In this example (size = 7), we want to print spaces at an even distance (0, 2, ...) and #s at an odd distance (1, 3, ...). But if you draw out the square for size = 9, you'll see this is reversed: even distances should be #s and odd distances should be spaces. So what's the relationship here (again, in terms of size, which is our only parameter from the user)?

Looking through my existing variables, I found a correlation with halfSize: if halfSize is odd, then all even-distanced characters (including the center character) should be spaces. Other characters--odd-distanced characters--should be #s.

On the other hand, if halfSize is even, then even-distanced characters (and the center character) should be #s.

So you can think about this over, here's all three images together:

Three ways to look at the problem

As always: This is just one way to solve this problem. I am sure there are others.

Okay, I think I know what I'm supposed to do... but how do I turn this into code?
First, work on the problem set above.

If these give you trouble, look at the sample solutions (once posted). Make sure you understand how each for loop works. That doesn't mean stare glassy-eyed at it for a minute and then maybe run the program to see it prints some stuff. For those you don't understand, you may need to get out a piece of paper and pretend you are a computer. Pick an example value for width--like 3 or 4. Then trace through the code of each example like I do on the board in lab: line-by-line, drawing a box for each variable declared and tracking how its value changes and what gets printed to the screen. Once you understand how these examples work, it'll be a lot easier to write your own solution.

Next, get out a piece of paper and plan your algorithm. The process I went through is outlined above, but you're still going to need to understand it. Draw some sample output. Sketch out your code structure without worrying about syntax: where do your for loops go? Which one is x and which one is y? Where do your if statements go? Trace through it manually to see if your logic is correct.

Once you feel you understand the problem, then you can finally open a text editor and start writing code. I'd still recommend taking it step-by-step:

How did that double for loop go?
Section 001 & 003: In lab, I showed you this:
for (int y = 0; y < width; y++) {
  for (int x = 0; x < width; x++) {
    System.out.print("*");
  }
  System.out.println();
}

Assuming width is a user-specified value, this will print a box of *s of that size. (See example Problem 3 above.) Note that both of these loops start at 0 and go through 1 less than width. So the inner loop prints each row, and the outer loop specifies how many rows to print. Therefore, each character printed will have a unique (x, y) value when printed.

To solve the other sample problems and A13, you can just use a conditional within the inner loop to decide what character to print for the current (x, y). That is, you want to print only one character each time through the inner loop, but that character might a "#" or it might be a " " depending on the current (x,y) position.