diff --git a/assign2.md b/assign2.md new file mode 100644 index 0000000000000000000000000000000000000000..19bc1bd635add073bdae986028bef13aead139fe --- /dev/null +++ b/assign2.md @@ -0,0 +1,189 @@ +# Assignment 2: Triples +**Due: March 8, 2020, 11:59:59 pm, v1.0** + + +## Goals +Use the routines you implemented for assign1 in building the *Triples* game. + +## Creating the Project +**Note:** create your new project **assign2** directly in your repository, so the resulting directory +structure should look like this: +``` + repository + / \ + assign1 assign2 + / \ / \ + assign2 assign2.xcodeproj/ + / \ + model.swift ... +``` +Also: +- Do not select either unit tests or automated tests, we will not use them. +- Do **not** select "Create git repository". + +## Game Rules +1. The board consists of a 4x4 grid of rectangles. +1. Move the board contents Up, Down, Left, and Right, combining compatible numbers into their sums. +1. The game's "goal" is to achieve the highest score, and the game continues as long + as the player is able to keep moving. + +## Board Details +First, set your simulated phone to an **iPhone 11**. This will give us a +common platform. + +The elements of your view should be the following: +- 16 rectangular "tiles", implemented by buttons. +- A button labeled 'New Game' +- Buttons labeled 'Left', 'Right', 'Up', and 'Down'. +- A segmented control, consisting of panes "Random" + (random), and "Determ" (deterministic). Check for deterministic running as + `segmentedOutlet.selectedSegmentIndex == 1`, where `segmentedOutlet` is an outlet you + ctrl-dragged from your new segmented + button. +- A label where you will show the current score. + +The actions should be as follows: +- Activating the `New Game` button should cause the game to be restarted, calling `newgame()` with the `rand` initializer + argument (see below) defined by the current state of the switch, followed by *four* `spawn()`s. +- Activating directional arrows should cause a *collapse* towards the indicated direction, followed +by a spawn *if the collapse modified the board*. +- Changing Random/Determ does not affect the current game, it only changes subsequent games. +- A running score should be shown in the label. + + +## Model Details +Define your model by augmenting the `Triples` model from **assign1** to +ensure the following properties are externally visible: +- `var board: [[Int]]` +- `var score: Int` + +Property `score` shows the current game score, calculated by incrementing your total with +the value of each new number that appears in the game. For example, spawns of a '1' and a +'2' will add 1 and 2 points, respectively. Combining a '2' and a '1' into a '3' adds 3 +points. Combining two '3's into a '6' adds six points, etc. Look at the running point +count in the video if this is unclear. + + +Your model should also have the following externally-visible methods: +- `newgame(rand: Bool)` - reinitializes all model state. The value of `rand` + should be derived from the Random/Deterministic segmented button. Note that this method + should *not* call `spawn()` (calling `newgame` just initializes an empty game). Instead, + the "action" for the newgame button should call + `newgame()`, followed by four `spawn()`s. +- `collapse(dir: Direction) -> Bool`, collapse the model's board in the indicated +direction, in place. Return `true` if movement occurred. You should +define Direction to be an enum consisting of "Left", "Right", "Up", and +"Down". Coincidentally, these are also the labels of the directional buttons. +- `spawn()`: which randomly chooses to create a new '1' or a '2', and puts it in an open + tile, if there is one. See `Randomized Spawn` below. +- `isDone()`: returns true if there are no more possible moves. + +Your model may have other externally visible controls and state, only the above is required. +All of this state should be kept in the `Triples` class defined in a file called `model.swift`. + +## Game Play +- Pressing `New Game` should produce a brand new game on the display, w/ four + newly-spawned tiles, each either a "1" or a "2". +- Pressing `Left`, `Right`, `Up`, or `Down` should cause the underlying model to react as + in `assign1`. The visible buttons should correspond to that model, and the score should + be updated. + - A new tile is spawned afterwards if at least one tile has moved or combined with + another. + - When your app determines that there are no more possible moves in any direction it + displays an alert showing the current score, with an action that allows the alert to + be dismissed. +- Pressing either `Random` or `Determ` (deterministic) controls the random seed used by + future new games. See below. + + +### Randomized and Deterministic Spawning +Swift 4.2+ has a beautiful new way of creating random values (e.g. `Int.random(in: +1...10)`). We aren't going to use that. +The reason is that we want to allow the game +to be made repeatable for debugging and testing (including our +testing). Define the following in your model: +``` + func prng(max: Int) -> Int { + let ret = Int(floor(drand48() * (Double(max)))) + return (ret < max) ? ret : (ret-1) + } +``` +The `drand48()` method is a +pseudo random number generator (PRNG) deterministically generates a random-ish +sequence of values from an initial seed. + +Use `prng` in all cases to get random integers for this project. Seed `prng` +in `newgame()`, using `srand48(42)` when running deterministically, or +`srand48(Int.random(in:1...1000))` when in random mode. This latter +approach is seeded from operating system sources and is easily random enough for your +needs. + + +Your app has only two randomized decisions: whether to spawn a '1' or a '2', and where to +put the newly spawned value: +- '1's and '2's have equal probability. +- Choose the open tile to filled by the following: + - number the open tiles by traversing the grid by row, and left-to-right within each + row. The first open tile has index `0`. +For example, with: +``` + | 0 1 0 2 | + | 0 0 3 6 | + | 1 2 1 1 | + | 3 3 0 3 | +``` + +Conceptually number these open spots as follows: +``` + | 0 1 | + | 2 3 | + | | + | 4 | + +``` +Choose among them by randomly selecting in indice from 0 to 4. + +Use `prng` to make both random choices. We want your deterministic mode to +exactly match ours, so use the following ordering: +- First check to see if there's an open spot for a new tile. Do not call `prng` otherwise. +- Use `prng` to decide the value of the new tile. +- Use `prng` to decide where to place the new tile. +This ordering is important; otherwise your random decisions may not match +ours. **Your gameplay must match the below video** for full credit. + + + + + +### Grading / Testing +Our grading will once again be automated grading. We will soon release two scripts +that you can run to test your interfaces and gameplay. We will use those scripts +and two others for our automated testing. + +We will award points as follows: + +- (5 pts) Display is layed out with correct controls. +- (5 pts) Gameplay is correct, numbers move as expected, combine as expected. +- (5 pts) Scores are correct. This can be checked even with out determinism, but in + deterministic mode: + - click "New Game". Should have a score of 7. + - new game / down / down / down / down / right should give a score of 26 +- (5 pts) Click new game (in random mode) multiple times and see different initial + conditions, all with 4 buttons displayed, each either a '1' or a '2' +- (10 pts) Determinism works: + - in deterministic mode a new game reproduces the sequences shown above, i.e.: + - new game / right / right / right gives the "C" shape, and + - new game / down (9 times) results in + the two rows (plus 1 extra tile) shown in the movie above +- (10 pts) Overall look and feel. It's an app! Your app should be well layed + out, clear, and attractive. We are not grading how closely it looks to the one shown + above, though the three classes of colors (ones, twos, others) should be distinct. + Your app should be attractively layed out. + +Notes: +- Your game should look relatively close to the above video in coloration and layout. +- You do not need to worry about orientation changes. +- We are not looking at your code other than to look for plagiarism. +- We do not care if you implement the "tiles" by changing the appearance of 16 static + buttons (easiest), or if you use fewer buttons and move them about. +