diff --git a/assign1.md b/assign1.md new file mode 100644 index 0000000000000000000000000000000000000000..54b3feb48ccfa664a6b4d2aa193c8120270e3efb --- /dev/null +++ b/assign1.md @@ -0,0 +1,231 @@ +# Assignment 1: Swift and Extensions +**Due: Feb 16, 2020, 11:59:59 pm, v0.3** + + +## Goals +The primary goal of this project is to to familiarize yourself with the environment and +the language. All in this class will be written in Swift 5.1, using XCode 11 as your +friendly development environment. *Errors resulting from development in different +environments still result in loss of points.* More importantly, `SwiftUI` will not work +on anything before Mojave. + +Specifically, you will do the following: +1. create a standard single-view app, "*assign1*" that does nothing (other than +compile and show a blank screen), but does include a model of our app: "Triples". + +In the process you will obviously learn quite a bit about the `XCode` environment, about +unit tests in the context of Swift applications, and about defining type extensions. + +## Using git +We will use [*git*](https://git-scm.com/doc) ([cheat sheet](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=2ahUKEwi_uaemsqXgAhVmzlkKHc-HCUkQFjABegQIAhAC&url=https%3A%2F%2Feducation.github.com%2Fgit-cheat-sheet-education.pdf&usg=AOvVaw2D3W2R0fwoOBi8YrhZYLFJ)) both to distribute starting files, for those projects that +have them (not `assign1`), and to submit the work that you create. Each of you will have had a single +gitlab repository create, e.g. `ios436spring2020/cmsc436-<dirid>` (but use your +directoryid, which is NOT a number). Clone this repository as follows: +1. log into gitlab and verify that you have the above repository +1. create and upload a public key as described below in "Upload Public Key". This will allow you to + access gitlab via `ssh`, which is much less kludgy/buggy than https. +1. In your terminal window, clone via `git clone git@gitlab.cs.umd.edu:ios436spring2020/cmsc436-<dirid>` (again, use *your* +dirid). **cd** into this directory. + - **Note** that if your repository is not found, or you don't have + permissions, it's because our script could not finish setting the + repository up until you had logged into gitlab.cs.umd.edu at least + once. Please do this at once, and then try again the next day. If it + still fail, email me directly w/ subject "436 repository". +2. Establish a new *upstream remote*: `git remote add upstream + git@gitlab.cs.umd.edu:keleher/cmsc436spring2020-starter.git` This remote + will be where you *pull* startup files from when each new project, or iteration of a + project, is made available (`git pull upstream master'). **You will not + need the upstream remote for this project, but set it up anyway.** +3. Verify your setup by executing `git remote -v`. You should see something +like: +``` +origin git@gitlab.cs.umd.edu:ios436spring2020/cmsc436-<dirid>.git (fetch) +origin git@gitlab.cs.umd.edu:ios436spring2020/cmsc436-<dirid>.git (push) +upstream git@gitlab.cs.umd.edu:keleher/cmsc436spring2020-starter.git (fetch) +upstream git@gitlab.cs.umd.edu:keleher/cmsc436spring2020-starter.git (push) +``` + +Uploading your completed work is a two-step process: `git commit -m "a +label"`, followed by `git push origin master`. Verify by logging into the +gitlab website and making sure your new changes are there. + +It's a good idea to +commit intermediate versions of your code many times during the development process. + + +### Upload Public Key +If you don't already have an RSA public key-pair, do the following: +``` + > cd + > ssh-keygen -t rsa + (hit return in response to all prompts) +``` +Upload the contents of `~/.ssh/id_rsa.pub` to gitlab by logging in, +selecting 'Settings' from the menu in the upper right, and then selecting 'ssh +keys' from the left of the page. Add your key by pasting into the textbox, +giving your machine's name as a title, and clicking 'Add Key'. + + +# Building An App +We are going to build an apple called `Triples` in two or three stages. In the first +stage, we are just going to design and implement the model, and unit-test it. + +As we did in class, you will create a new single-view iOS app: +- Create a new project in XCode and save to your `cmsc436-<dirid>` directory. +- When creating the project: + - also call it `assign1` + - **check "unit tests"**, uncheck `git`, uncheck UI tests + - select a "single-view" iOS app even though we will not have any graphical characteristics +- Note that new files must be explicitly added to git via `git add <file>...`. + +Create a new file `model.swift` in the app by selecting "New File" from the, and then +selecting the Swift file icon in the dialog, and name it `model.swift`. +`model.swift` will contain the "model" known from the model-view-controller (MVC) paradigm. + +## App Logic +Our game action will be similar to the app `Threes` app on the apple and android app +stores. A portion of the game play is shown here: + + + + +## Your Tasks +Your tasks are to implement the *model* behind the gameplay shown in the clip. Specifically, you will +implement the "collapse", which given a swipe in a direction collapses each row or column +parallel to the swipe by eliminating a blank, combining a 1 and a 2, or combining two +consecutive tiles w/ the same value, 3 or higher. For more specifics, see the tests +described below. + +## The `Triples` Class + +In the file `model.swift` you need to define a new class `Triples`, +which implements the methods discussed below. Most importantly, +`Triples` will need the following definitions: +``` + var board: [[Int]] + + func newgame() {} // re-inits 'board', and any other state you define + func rotate() {} // rotate a square 2D Int array clockwise + func shift() {} // collapse to the left + func collapse(dir: Direction)) // collapse in specified direction using shift() and rotate() +``` +Note that methods merely modify the `board` property. We are not taking any +actions based on the board, or doing any GUI work at all. +`Direction` is an `enum` that you will define for the four directions referenced below. + +Outside `Triples`, you will need to build two other functions, one of which +should be called in your `rotate()` method (ideally the generic, but the +explicit `Int` version is fine if you don't get the generic working). +The first is: +``` + // class-less function that will return of any square 2D Int array rotated clockwise + public func rotate2Dints(input: [[Int]]) -> [[Int]] {} +``` +You should also build `rotate2D`, which is a *generic* version of the above. + +### Unit Tests +Copy the following unit tests verbatim into your file +`assign1Ttests.swift`. Do no change anything, as we will use the exact tests +below to test your definitions in `model.swift`. +``` + func testSetup() { + let game = Triples() + game.newgame() + + XCTAssertTrue((game.board.count == 4) && (game.board[3].count == 4)) + } +``` +Tests to ensure your app defines a 4x4 2D array of `Int`s. + +### Rotate +Build a func `rotate2DInts` that will take any 2D Int array and rotate it once +clockwise. Your func may assume that the array is square. +``` + func testRotate1() { + var board = [[0,3,3,3],[1,2,3,3],[0,2,1,3],[3,3,6,6]] + board = rotate2DInts(input: board) + XCTAssertTrue(board == [[3,0,1,0],[3,2,2,3],[6,1,3,3],[6,3,3,3]]) + } +``` + +### Generic Rotate +Same test using a generic func you must write called `rotate2D`, and which will work on +any type of array. +``` + func testRotate1() { + var board = [[0,3,3,3],[1,2,3,3],[0,2,1,3],[3,3,6,6]] + board = rotate2D(input: board) + XCTAssertTrue(board == [[3,0,1,0],[3,2,2,3],[6,1,3,3],[6,3,3,3]]) + } +``` + +Try again with strings. +``` + func testRotate3() { + var board = [["0","3","3","3"],["1","2","3","3"],["0","2","1","3"],["3","3","6","6"]] + board = rotate2D(input: board) + XCTAssertTrue(board == [["3","0","1","0"],["3","2","2","3"],["6","1","3","3"],["6","3","3","3"]]) + } +``` + +### Collapsing +Write a `shift()` method that collapses each row to the left (as if w/ a left swipe in the +clip). +``` + func testShift() { + let game = Triples() + game.board = [[0,3,3,3],[1,2,3,3],[0,2,1,3],[3,3,6,6]] + game.shift() + XCTAssertTrue(game.board == [[3,3,3,0],[3,3,3,0],[2,1,3,0],[6,6,6,0]]) + } +``` + +Write and test a `collapse()` method will take a `Direction` enumeration collapse in the +direction specified. Internally, you should build `collapse` using `shift()` and `rotate()`. +``` + func testLeft() { + let game = Triples() + game.board = [[0,3,3,3],[1,2,3,3],[0,2,1,3],[3,3,6,6]] + game.collapse(dir: .left) + XCTAssertTrue(game.board == [[3,3,3,0],[3,3,3,0],[2,1,3,0],[6,6,6,0]]) + } + + func testRight() { + let game = Triples() + game.board = [[0,3,3,3],[1,2,3,3],[0,2,1,3],[3,3,6,6]] + game.collapse(dir: .right) + XCTAssertTrue(game.board == [[0,0,3,6],[0,1,2,6],[0,0,3,3],[0,3,3,12]]) + + } + + func testDown() { + let game = Triples() + game.board = [[0,3,3,3],[1,2,3,3],[0,2,1,3],[3,3,6,6]] + game.collapse(dir: .down) + XCTAssertTrue(game.board == [[0,3,0,0],[0,2,6,3],[1,2,1,6],[3,3,6,6]]) + } + + func testUp() { + let game = Triples() + game.board = [[0,3,3,3],[1,2,3,3],[0,2,1,3],[3,3,6,6]] + game.collapse(dir: .up) + XCTAssertTrue(game.board == [[1,3,6,6],[0,2,1,3],[3,2,6,6],[0,3,0,0]]) + } +``` + +# Submit, and Grading +Submit by pushing your project to your 436 repository. **Check this** by visiting your +repository in a web browser at `https://gitlab.cs.umd.edu/ios436spring2020/cmsc436-<dirid>` + +Each of the nine tests is worth 1 point. There is no partial credit. + + +# Notes +- Remember, frequently save your work on gitlab through the folloing. This is exactly the same sequence you will use with the final submit. We will download + your repository from gitlab directly. +``` + git add assign1 + git commit -m "a message" + git push origin master +``` +