CPSC 124 Introduction to Programming Spring 2024

Lab 5
Arrays

Due: Tue 2/27 at the start of lab


Introduction

This week's lab focuses on arrays. The power of arrays comes from the ability to refer to a particular slot using an integer index - because that index can be calculated as the program runs. This lab will give you some practice with two major ways in which arrays are used: "sequential access" applications where arrays are used to conveniently manipulate collections of things (such as in the Reverser example from class), and "random access" applications where the ability to calculate array indexes is exploited (such as in the DiceCounter example from class).

Collaboration

Labs and projects are for practice and learning. While it can be very productive to work on problems with your peers, it is also easy to underestimate how much you yourself understand and can do in such situations — so often something looks easy when someone else does it! With this in mind, you should always make the first attempt on a problem or programming task yourself using only the resources provided for the course (textbook, slides and examples posted on the schedule page, other resources linked on the course webpages). After that point, you are encouraged to come to office hours and/or go to Teaching Fellows. You may not collaboratively write solutions or code, and you may not copy solutions or code written by others, even if you contributed ideas.

You can discuss specific exercises with other students in general terms — such as how you might get started on that problem, or how or when to use various programming constructs, or strategies for debugging — but how to use a particular programming construct to solve a specific problem or debugging a particular program should only be discussed in office hours or with the Teaching Fellows.


Setup


Programming With Arrays

Arrays are variables rather than statements like if statements and for/while, so in most cases you need to first recognize that you need to store information (what variables are for) and then the question is whether you have many values of the same kind (both Java type and in terms of meaning). If so, an array is appropriate; if not, you should have separate variables.

Once you have determined that an array is appropriate, identify the pattern of usage:

If you are working with a collection of things as a collection, you will fill up the slots of the array from the beginning and will typically write loops to do something to each slot of the array (initialize it, update it, print it, etc). (This is "sequential access".) Recall the "for each slot of the array" pattern from class to get you started when you have a "for each slot of the array" task in your program.

If you are doing clever indexing (i.e. "random access"), consider how you will use the slots of the array - what values will go where? For example, in DiceCounter, the count of the number of times the sum sum was rolled was stored in slot sum. In the birthday example in the reading, the days of the year were numbered 0 to 364 and the corresponding slot in the array held whether or not someone with that day as a birthday had been found already. Once you have what values are stored where worked out, usage of the array in the program boils down to computing the right index when you need to refer to a slot.

Note that these patterns are not mutually exclusive. For example, you will often use a "for each slot of the array" loop to initialize the array in a clever indexing application. DiceCounter has this, and a modified version of the "for each slot" loop to print out the counts at the end.

While most of the time an array is used to facilitate management of what would otherwise be a bunch of separate but similar variables, an array can also be used to facilitate sequential or random access to what would otherwise be a bunch of separate values.

In the sequential case, an array can be used to allow loops when the change between repetitions doesn't have a nice pattern, such as in the ColorSpots example from class. An example of the random access case will be seen in the exercises.

As this usage of arrays doesn't stem from needing to store information, it needs to be recognized on its own.


Exercises

For all exercises:


Exercise #1

A histogram is a graph that shows the frequency of numerical data.

For example, for an array { 17, 8, 0, 5, 14, 9, 6, 6, 3, 6 }:

(17) #################
( 8) ########
( 0) 
( 5) #####
(14) ##############
( 9) #########
( 6) ######
( 6) ######
( 3) ###
( 6) ######

Each line should start with the number in the array in parens, followed by a space and a number of # symbols equal to that value. (Use formatted output as described in section 2.4.1 to pad 1-digit numbers with spaces so that things line up nicely.)


Exercise #2

A Magic 8 Ball can be used to answer life's difficult questions: simply ask it a yes-no question, and it will reply.

The possible responses are:

(Note that the program doesn't actually do anything with the question the user types in — it reads it in, but no use is made of that value later in the program.)

Your program must be reasonably compact and elegant — use an array in order to avoid a big if statement to choose a response, and write it so that if you wanted to change the possible responses (either the number of responses or what the responses are), you'd only have to change the array initialization.

But how to use an array? Think about this before revealing what is below.


Exercise #3

In the dice game Pig, a player's turn consists of rolling a single die until either a 1 is rolled or the player chooses to stop rolling. If a 1 is rolled, the player scores nothing for that turn. If the player chooses to stop rolling, the sum of all of the values rolled on that turn is added to the player's score. Players take turns, and the first player to accumulate 100 points wins.

Your program's output should be similar to the following: (user input is indicated with bold underline)

enter the number of players (at least 2): 1
  invalid number!
enter the number of players (at least 2): 3

player 0, do you want to roll?  [y/n]  y
  you rolled: 6
player 0, do you want to roll?  [y/n]  y
  you rolled: 1
  end of turn

current scores:
  player 0: 0
  player 1: 0
  player 2: 0

player 1, do you want to roll?  [y/n]  y
  you rolled: 6
player 1, do you want to roll?  [y/n]  y
  you rolled: 6
player 1, do you want to roll?  [y/n]  n
  you scored 12 points on this turn

current scores:
  player 0: 0
  player 1: 12
  player 2: 0

player 2, do you want to roll?  [y/n]  y
  you rolled: 3
player 2, do you want to roll?  [y/n]  y
  you rolled: 1
  end of turn

current scores:
  player 0: 0
  player 1: 12
  player 2: 0

player 0, do you want to roll?  [y/n]  y
  you rolled: 5
player 0, do you want to roll?  [y/n]  y
  you rolled: 1
  end of turn

current scores:
  player 0: 0
  player 1: 12
  player 2: 0

(...the game goes on for a while...)

player 2, do you want to roll?  [y/n]  y
  you rolled: 3
player 2, do you want to roll?  [y/n]  y
  you rolled: 2
player 2, do you want to roll?  [y/n]  y
  you rolled: 4
player 2, do you want to roll?  [y/n]  y
  you rolled: 5
player 2, do you want to roll?  [y/n]  n
  you scored 14 points on this turn

current scores:
  player 0: 95
  player 1: 60
  player 2: 78

player 0, do you want to roll?  [y/n]  y
  you rolled: 4
player 0, do you want to roll?  [y/n]  y
  you rolled: 6
player 0, do you want to roll?  [y/n]  n
  you scored 10 points on this turn

player 0 has won with 105 points!

Be sure to include all of the elements shown:

As with other more complex programs, start by outlining the necessary steps in English first. Also consider how arrays are useful and what array usage pattern is applicable before revealing what is below.

As you start to translate the English steps into code, keep in mind the idea of solving smaller, simpler problems first. For example, you could start with getting a valid number of players. When that works, do player 0's turn. When that works, update player 0's score and print all the player scores. Then repeat the turn and score printing until the game is over. Finally, detect and congratulate the winner. An alternative approach is to start with a single-player version of the game: take turn, print score, take turn, print score, etc until the one player's score gets to 100. When that works, expand to multiple players by repeating the one player actions for each player.


Exercise #4

Imagine a promotion in which every time you buy a product, you get one of 8 different prizes. On average, how many products do you have to buy in order to collect a full set of the prizes?

Armed with an understanding of probability, you could derive a formula giving the answer. You can also solve the problem by writing a program to simulate the prize-collection process: generate random numbers to represent which prize is obtained each time you make a purchase, and count how many times that is done before you get all the prizes. Then repeat the whole process many times and compute the average number of purchases made to obtain the final answer.

How are arrays useful here? (What do you need to store that you have many of? Can you think of an example of a similar situation and how the problem was solved in that case?) What array usage pattern is this? Develop the pseudocode for the program and think about these answers before revealing what is below.

As with other more complex programs, start by outlining the necessary steps in English first. Cover the whole program at a broad level, then work on writing the code for a simpler version of the problem: get one round working (choose random prizes until all of the prizes have been obtained, then print the number of purchases needed) first. Once that works, repeat one round the desired number of times (still printing the number of purchases each time), then finally add in what is needed to compute the average.

For extra credit, create a separate program PrizeCollectionSimGrapher which combines this exercise with #1: ask the user for a number N, compute the average number of purchases needed for each number of prizes between 2 and N, and then show these values using a histogram. Scale the number of # symbols printed — if x purchases are needed, print 2x # symbols.


Handin and Grading

Don't forget to hand in your work when you are done!

As in lab 2 (and all labs), grading will be based on both functionality and style. Style means following the good programming style conventions outlined in lab 2, as well as producing reasonably compact and elegant solutions. "Reasonably compact and elegant" means that your program is not significantly longer or more complex than it needs to be. In particular, you must use loops when loops are appropriate — achieving repetition by copying-and-pasting a bunch of code will not earn credit.