Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cmsc436PublicF2021
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Peter Keleher
cmsc436PublicF2021
Commits
413da408
Commit
413da408
authored
3 years ago
by
Peter J. Keleher
Browse files
Options
Downloads
Patches
Plain Diff
auto
parent
56c909c4
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
assign2/README.md
+214
-0
214 additions, 0 deletions
assign2/README.md
assign2/assign2.mp4
+0
-0
0 additions, 0 deletions
assign2/assign2.mp4
assign2/assign2.png
+0
-0
0 additions, 0 deletions
assign2/assign2.png
with
214 additions
and
0 deletions
assign2/README.md
0 → 100644
+
214
−
0
View file @
413da408
# Assignment 2: Twos
## Goals
Use the routines you implemented for assign1 in building the
*Twos*
game.
Specifically, you will create an application having a model that emulates the logic of the 2048
app
[
here
](
https://play2048.co/
)
in a SwiftUI app.
A brief video demonstrating the main points of the project is at the bottom of
this page.
## Creating the Project
You will once again need to create a Swift project in Xcode.
For this project, do not select "Include tests", since we will not use them.
## Game Rules
1.
The board consists of a 4x4 grid of rectangles.
2.
Move the board contents Up, Down, Left, and Right, combining compatible
numbers into their sums.
3.
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 12**
. This will give us a
common platform.
The elements of your view should be the following:
*
4 X 4 rectangular "tiles", implemented by
`Text View`
.
*
A
`Button`
labeled 'New Game'
*
`Button`
s labeled 'Left', 'Right', 'Up', and 'Down'.
*
A
`Picker`
or
`Toggle`
, consisting of options "Random"
(random), and "Determ" (deterministic).
*
A
`Text View`
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 picker or toggle, followed by
*two*
`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 text view.
## Model Details
Define your model by augmenting the
`Twos`
model from
**assign1**
to
ensure the following properties are externally visible:
*
`var board: [[Tile]]`
*
`var score: Int`
Property
`score`
shows the current game score, calculated by
incrementing your total by the resulting sum each time two identical numbers combine.
For example, initial spawns of two '2's leaves your score at 0, but if you
combine them in the next move that will add 4
points to your score. Combining two '4's into an '8' later on adds 8
points, etc.
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 two
`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 '2' or a '4', and puts
it in an open tile, if there is one. See
`Randomized and Deterministic Spawning`
below.
Your model may have other externally visible controls and state, only
the above is required. All of this state should be kept in the
`Twos`
class defined in a file called
`model.swift`
.
## Game Play
*
Pressing
`New Game`
should produce a brand new game on the display, with two
newly-spawned tiles, each either a "2" or a "4".
*
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 has a beautiful new way of creating random values (e.g.
`Int.random(in:... , using: ...`
). However, we are going to create our own pseudorandom generator
for this method to make the game repeatable for debugging and testing.
You need to create a new file:
`Random.swift`
, and copy the code below:
```
struct SeededGenerator: RandomNumberGenerator {
let seed: UInt64
var curr: UInt64
init(seed: UInt64 = 0) {
self.seed = seed
curr = seed
}
mutating func next() -> UInt64 {
curr = (103 &+ curr) &* 65537
curr = (103 &+ curr) &* 65537
curr = (103 &+ curr) &* 65537
return curr
}
}
```
To create a pseudorandom generator with Seed = 14, your code should be written like this:
```
var seededGenerator = SeededGenerator(seed: 14)
```
To generate a number in 1 to 16 by using this generator, your code would look like:
```
var newRandomNumber = Int.random(in: 1...16, using: &seededGenerator)
```
More generally, you can define (if you prefer)
```
var generator: RandomNumberGenerator = SeededGenerator(seed: 14)
var newRandomNumber = Int.random(in: 1...16, using: &generator)
```
This allows you to use the same syntax with the system random number generator:
```
generator = SystemRandomNumberGenerator()
```
which will provide better randomness. We will refer to
`seededGenerator`
below, but you can use either form.
Your app has only two randomized decisions: whether to spawn a '2' or a '4',
and where to put the newly spawned value:
*
'2's and '4's have equal probability.
*
Choose the open tile to fill 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 2 0 2 |
| 0 0 4 8 |
| 2 2 4 8 |
| 4 2 0 4 |
```
conceptually number the open spots:
```
| 0 1 |
| 2 3 |
| |
| 4 |
```
Choose among them by randomly selecting an index from 0 to 4. You can do this by
running
`index = Int.random(in: 0...4, using: &seededGenerator)`
.
Use the randomGenerator to make both random choices. We want your deterministic
mode to exactly match ours,
**so use the following ordering**
:
*
Use
`seededGenerator`
to decide the value of the new tile.
*
Use
`seededGenerator`
to decide where to place the new tile.
This ordering is important; otherwise your random decisions may not match ours.
For this assignment, you should:
*
Reinitialize seededGenerator each time starting a new game
*
Use either a random seed in 1 to 1000 if your game mode is
`random`
or the system
random number generator. You can use a random seed by running:
`seededGenerator = SeededGenerator(seed: UInt64(Int.random(in:1...1000)))`
*
**Important**
Use seed = 14 if your game mode is
`determ`
### Grading / Testing
Our grading will not be automated.
We will award points as follows:
*
(10 pts) Display is layed out with correct controls.
*
(5 pts) Click new game (in random mode) multiple times and see different
initial conditions, all with 2 buttons displayed, each either a '2' or a '4'
*
(10 pts) Gameplay is correct, numbers move as expected, combine as expected.
*
(5 pts) Scores are correct. This can be checked even without determinism,
but in deterministic mode:
*
Click "New Game". Should have a score of 0.
*
Click "new game, right, right, up, up, right, right" should give a score of 36.
*
(10 pts) Determinism works:
*
in deterministic mode a new game reproduces the sequences shown above, i.e.:
1.
Click "new game, right, right, up, up, right, right" results in a
score of 36, and tiles 16, 2, 4, and 4.
2.
See demo video for details.
*
(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 in the video, though different values should have distinct
colors, at least through 128.
Notes:
*
Your game should look relatively close to the provided video in coloration
and layout.
*
You do not need to worry about orientation changes.
*
We are not looking at your code other than to scan for plagiarism.
*
We do not care if you implement the "tiles" by changing the appearance
of 16 static views (easiest), or if you use fewer buttons and move
them about.
[

](assign2.mp4)
This diff is collapsed.
Click to expand it.
assign2/assign2.mp4
0 → 100644
+
0
−
0
View file @
413da408
File added
This diff is collapsed.
Click to expand it.
assign2/assign2.png
0 → 100644
+
0
−
0
View file @
413da408
1.19 MiB
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment