Random Number Generation in Java
The main program fills the 16 by 20 grid with random colors first, then runs a while loop that repaints the current square and takes one random step while the window is open.
A Mosaic That Never Stops Walking
This program opens a window divided into a grid of colored squares. It paints every square a random color, and then it lets a single point of interest wander across the grid, repainting each square it lands on. The walk never stops until you close the window, so the final picture is a mosaic whose colors depend on where the walk happened to go.
The best way to read a program like this is to watch it run and then ask what each part of the code must have done to produce what you saw. Keep one question in mind throughout: if the window ever shows something you did not expect, which subroutine would be responsible for it?
What do you think happens?
The disturbance is already on row 0, and the random direction says to move up. Which row does it end up on?
Reveal answer
Row 15
A grid has no row above row 0, so the out-of-range row is replaced with the value on the opposite edge. The grid has 16 rows, numbered 0 through 15, so the opposite edge is row 15.
Filling the Grid Before the Walk
The first real work the main program hands off is the fill step. The fillWithRandomColors subroutine uses two nested loops. The outer loop counts rows from 0 up to 15, and the inner loop counts columns from 0 up to 19. Together they visit every square of the 16 by 20 grid exactly once. For each square, the subroutine calls changeToRandomColor with that square's row and column.
- The outer loop visits rows 0 through 15
- The inner loop visits columns 0 through 19 for each row
- Each square is visited exactly once
The nested loops are a simple way to guarantee coverage. The count of 16 rows and 20 columns appears as literal numbers in the code. The source passes these values directly rather than defining named constants, so the same numbers must be kept consistent by hand in every place they are used.
From Math.random to Colors and Directions
The color itself is produced inside changeToRandomColor. It needs three numbers, one each for red, green, and blue, and each number comes from the same expression pattern. Math.random() produces a double in the range from 0.0 up to, but not including, 1.0. Multiplying that by 256 stretches the range to roughly 0 up to 256. The cast to int then drops the fractional part, leaving whole numbers from 0 to 255. Those are exactly the values a color component can take.
Math.random()returns a double from 0.0 up to but not including 1.0- Multiplying by 256 stretches that range to about 0 to 256
- The cast to int keeps only the whole part, giving 0 to 255
The three values are then passed to Mosaic.setColor along with the row and column. The direction is picked with the same idea but a different multiplier. The expression (int)(4*Math.random()) produces a whole number from 0 to 3, and the program stores that number in directionNum.
Why the Position Lives in Static Members
The program keeps track of the disturbance with two class-level variables, currentRow and currentColumn. They are declared with the static keyword outside any method. A reasonable first guess is that these should be local variables inside main. The source explains why that would not work: several different subroutines need each of these variables. main sets their starting values, and randomMove reads and updates them on every step of the walk.
A local variable exists only while the method that declares it is running, and each method has its own separate copy of its locals. If currentRow were local to main, randomMove would have no access to it. Making the variables static members of the class gives every static method in the class a shared place to read and write the current position. That shared place is what lets one subroutine move the disturbance and another subroutine repaint the square the disturbance is standing on.
Tracing One Call to randomMove
The randomMove subroutine performs exactly one step of the walk each time it is called. It begins by choosing a direction, and a switch statement then looks at directionNum and runs one of four cases. Case 0 moves up, case 1 moves right, case 2 moves down, and case 3 moves left. The direction numbers are chosen arbitrarily; any four distinct numbers would work, provided the switch matched them correctly.
Exactly one of the four branches runs for each call. Every branch changes only one of the two position variables, by one. After that, all four paths meet at the same edge check, and that shared check keeps the disturbance inside the grid no matter which direction was chosen.
- Each case moves the disturbance one square in one direction
- The comments describe the four directions in the program's own terms
- The shared edge check after the switch is not shown here
Tracing three steps of the walk
Start with currentRow = 0 and currentColumn = 0. Trace the position after each call to randomMove for the direction numbers 0, 1, and 3. Assume the example start position for illustration only.
- 1Step 1: direction 0 (up)
Row 0 minus one gives -1, which is outside the grid. The row is replaced with the value on the opposite edge, 15. Position is (15, 0).
- 2Step 2: direction 1 (right)
Column 0 plus one gives 1, which is inside the grid. Position is (15, 1).
- 3Step 3: direction 3 (left)
Column 1 minus one gives 0, which is inside the grid. Position is (15, 0).
After three calls the disturbance is back on square (15, 0), and the main loop would repaint that square before its next call to randomMove.
The Wrap-Around Rule at the Edges
The rule stated in the subroutine's documentation is that a neighboring position outside the grid is replaced by the position on the opposite edge. The four cases implement this rule with specific numbers. Moving up from row 0 sets the row to 15. Moving down from row 15 sets it to 0. Moving right from column 19 sets the column to 0. Moving left from column 0 sets the column to 19.
The column rule works the same way along the horizontal edges. Moving left from column 0 lands on column 19, and moving right from column 19 lands on column 0. Note that the wrap is only about the edge check: randomMove still changes exactly one position variable by one before the check runs.
Start at row 3, column 19. The random direction is 1 (right). Where does the disturbance end up, and which rule decides it?
Hints
- Right means the column goes up by one before the edge check
- Look at which edge rule applies when moving right from column 19
The Main Loop That Repeats
The main program does its setup once: it fills the grid with random colors and sets the starting position. After that, a while loop runs while the window is open. Each pass repaints the square at the current position with a new random color, and then calls randomMove to choose the next position. The loop does not stop on its own; it ends only when the window closes.
Mistakes Beginners Make
Common mistake Making currentRow local to main
What it looks likeint currentRow = 0; inside main, with randomMove expected to read itWhy it failsA local variable exists only while its own method runs, so
randomMovecannot see it.Do this insteadDeclare currentRow and currentColumn as static class members so every static method shares them.
Common mistake Dropping the cast to int
What it looks likeint red = Math.random() * 256;Why it failsThe product is a fractional double, and Mosaic.setColor is not written to accept it as a color level.
Do this insteadWrite (int)(Math.random() * 256), keeping the parentheses around the product.
Common mistake Assuming the disturbance stops at the edge
What it looks likeMoving up from row 0 and expecting row -1 to be drawn.
Why it failsThe walk never stops until the window closes, and the edge rule replaces an out-of-range position.
Do this insteadApply the wrap rule: row 0 moving up goes to row 15, and column 0 moving left goes to column 19.
Common mistake Changing the grid size in one place but not in the fill loops or the wrap checks
Practice Tracing the Walk
Use the rules from this article to trace the walk. Start with currentRow = 15 and currentColumn = 0. Apply these direction numbers in order: 2, 3, 0. After each step, state the new position and say which edge rule (if any) was used. Remember that the main loop repaints the current square before each call to randomMove.
Explain, in your own words, why the walk would break if currentRow were a local variable in main, and identify the one class of subroutines that would be unable to repaint the correct square.
Hints
- Think about which method needs the position to repaint the square
- Compare the scope of a local variable with a static class member
Key Takeaways
- The main program fills the 16 by 20 grid with random colors once, then a while loop repaints the current square and takes one random step until the window closes.
Math.random()returns a double from 0.0 up to 1.0; multiplying by 256 and casting to int gives a color level from 0 to 255, and multiplying by 4 and casting gives a direction from 0 to 3.currentRowandcurrentColumnare static class members because main andrandomMoveboth need them, and a local variable would be invisible torandomMove.randomMovechanges exactly one position variable by one, and an out-of-range row or column is replaced with the value on the opposite edge: row 0 going up becomes 15, row 15 going down becomes 0, column 19 going right becomes 0, and column 0 going left becomes 19.