CMU 15-112: Fundamentals of Programming and Computer Science
Homework 6b (Due Saturday 13-Mar at 8pm) (No Grace Day)
Important notes:
- This homework is collaborative. See the collaboration rules below, and see the syllabus for more details.
- We will discuss this problem as part of recitation on Wed 10-Mar, so you may plan accordingly. In any case, we encourage you to finish hw6a before starting hw6b.
- There is no grace day for hw6b.
Here are the rules for collaboration:
- Everyone must read all of these rules, even if you opt out of collaborating.
- Collaboration is optional.
- Collaboration is in groups of 2-3, arranged by your TA.
- If you do collaborate, you must arrange this with your TA from your cohort, who must know which students in the cohort are collaborating with which other students.
- You may only collaborate with other students in your cohort.
- You may not change your collaborators once you have chosen them (except with written permission from the faculty if your partner becomes nonresponsive).
- You must list the andrew id's of your collaborators in a comment just above your solutions to this section.
- You must be an effective collaborator, working together as much as possible.
- It is strictly forbidden for you to simply copy the code of your collaborators. That is not collaborating, that is cheating. Instead, you must type all your own code, without copying from others. You can discuss all parts of the problem with your collaborators. They can even help you debug. But still, you must write your own code.
- Reminder from the syllabus: when we say "do not copy" we always mean visually, verbally, electronically, or in any other way, even if you copy and modify it.
- Create a folder named 'week6'
- Download these files into that folder:
- Edit hw6b.py using VSCode
- When you have completed and fully tested hw6b, submit hw6b.py to Autolab. As usual, only your last submission counts.
- There is no autograder for hw6b. The TA's will manually grade your last submission sometime soon after the hw deadline.
- One-Dimensional Connect Four! [25 pts]
Note: this exercise is inspired by this wonderful project.
Starting from hw6b.py, write "One-Dimensional Connect Four" as shown in this video:
Here are some very important hints:- Watch the video, read the hints, etc
As with our hw3 app, be sure to watch the video carefully, then read all these hints carefully, and so on. Also, work on one feature at a time, to help manage the complexity. Basically, all the suggestions we made earlier still apply here. - Use indexes and not pixels as much as possible!
This is important! You do not want to store pieces by their location. You do not want your core logic to use (x,y) points, or pixels. Instead, you want to represent the pieces in a list (see next hint), and then your logic should be based on indexes into that list. So think of a piece as an index, not a (cx, cy, r) tuple. Now, you do have to deal with pixels, both to draw and also to handle mouse presses. Even so, as much possible, deal with indexes and not pixels. - How to store the board
This as a board game, like the standard Connect Four, only here it is one-dimensional. Thus, we describe the line of pieces as a board. We suggest you store the board in app.board, as a 1d list of 0's and 1's, representing the two players, though you may use other representations if you wish. - How to store the selection
The 3 pieces that are highlighted are called the "selection". We represented the selection by the index of the piece in the middle of the selection. We stored that in app.selectionCenterIndex. We also used app.selectionIsLegal to track if the selection is legal or not. - How to store the winning run
When there is a winner, the winning run is the sequence of 4 consecutive same-colored pieces through which a line is drawn. Similar to the selection, we stored this in an app variable. This time, we used the left edge of the run, calling it app.winningRunStartIndex. As usual, you can adapt (or ignore) this hint as you wish. - getPieceCenterAndRadius(app, pieceIndex)
We found this to be an important helper function. It takes the app and a pieceIndex, and returns (cx, cy, r), where that piece on the board is centered at (cx, cy) with radius r. We called this function in many places:- when we draw the board (we called it once per piece here)
- when we draw the winning line (we called it twice here, once for the leftmost piece in the winning run, and once for the rightmost piece)
- when we draw the selection box (again, we called it twice, for the two ends).
- in getPieceIndex() -- see next hint for details.
- getPieceIndex(app, x, y)
We found this to be another important helper function. It takes the app and x and y, and returns the index of the piece on the board that contains the point (x,y), or None if there is no such piece. While you do not need to do so, we found it helpful to call getPieceCenterAndRadius() here. In any case, this function was very helpful in mousePressed. - checkForWin(app)
Another helpful function, this takes the app and checks if there is a win (four in a row of the same color). If so, it sets app.gameOver, app.message, and app.winningRunStartIndex accordingly. - moveSelection(app, moveToLeftEnd)
Yet another helpful function, this takes the app and boolean indicating if we are moving to the left end (if False, then the right end). This is called whenever a player selects a piece on the end. If the selection is not legal, it sets the app message accordingly. Otherwise, this is where we change the board by removing the selection and then adding it back to the appropriate end. This function also calls checkForWin(). - Debugging features: c and p
Be sure to understand why it is so helpful to have these features: pressing c to set the color of selected block, and pressing p to change the current player. For example, these let you fairly quickly make blocks of 4 so you can verify that your app works properly when the game is over. - Drawing helper functions
Here is our redrawAll:def redrawAll(app, canvas): drawTitle(app, canvas) drawInstructions(app, canvas) drawCurrentPlayerAndMessage(app, canvas) drawBoard(app, canvas) drawRules(app, canvas)
- drawPlayerPiece(app, canvas, player, cx, cy, r)
This is a very helpful drawing helper function. It draws the given player's piece at (cx, cy) and with radius r. We used this to draw the pieces on the board, and then also to draw that extra piece that is drawn between the "Current Player" and the app.message. - Use text anchors
We usedanchor='e'
and alsoanchor='w'
in our calls to create_text in our sample solution. Be sure you understand what these do so you can use them as needed. - Colors
These are the colors we used:- 'lightBlue' with a 'blue' outline
- 'lightGreen' with a 'green' outline
- 'orange' and 'pink' for the selection box
- Message strings
These are the strings we assigned to app.message:- 'Select your 3-piece block'
- 'End cannot be in block'
- 'Block must contain current player'
- 'Select end to move block'
- 'Cannot move illegal selection'
- 'Game Over!!!!!'
- 'Select your 3-piece block'
- Instructions strings
Use this list in drawInstructions:messages = ['See rules below.', 'Click interior piece to select center of 3-piece block.', 'Click end piece to move that block to that end.', 'Change board size (and then restart) with arrow keys.', 'For debugging, press c to set the color of selected block.', 'For debugging, press p to change the current player.', 'Press r to restart.', ]
- Rules strings
And use this list in drawRules (and the somewhat-odd indenting is legal and avoids complaints by the linter):messages = [ "The Rules of One-Dimensional Connect Four:", "Arrange N (10 by default) pieces in a row of alternating colors.", "Players take turns to move three pieces at a time, where:", " The pieces must be in the interior (not on either end)", " The pieces must be adjacent (next to each other).", " At least one moved piece must be the player's color.", "The three pieces must be moved in the same order to either end of the row.", "The gap must be closed by sliding the remaining pieces together.", "The first player to get four (or more) adjacent pieces of their color wins!", ]
- More Hints
- We allow magic numbers for graphics layout.
- Board sizes are always even, between 6 and 20, inclusive.
- When the game starts, the leftmost piece must be randomly chosen, and the
player with the leftmost piece goes first.
- You might use
random.randint(0, 1)
to get a random integer between 0 and 1, inclusive. - Or, if you prefer, you might create a list and shuffle it, like so:
L = [0, 1] random.shuffle(L)
- You might use
- When you restart, the board size stays the same and does not reset to 10.
- When the board size is changed, restart with that new board size.
- Treat a click on an end piece as a move and not a new selection.
- When the game is over, the 'Game Over' message is drawn in the color of the winning player, next to the small piece of that player.
- If the winning run is longer than 4, draw the line through the leftmost 4 pieces.
- When the window is resized, the top text stays centered, the bottom text stays put on the left, and the board in the middle resizes -- the pieces get a bit larger or smaller, and stay evenly spread out across the entire canvas.
- Have fun!
For many of you, this will be the second real app you have ever made. Progress!!! Enjoy!!!
- Watch the video, read the hints, etc
- Hw6b Bonus Mode [up to 4 pts]
For a small amount of bonus points, but mostly for the fun of it, you may add some fun bonus features to your game. Just follow these guidelines so that we can find your features and give you proper credit for them:- If the user presses '!', print (in the console) a description of all of your bonus features. Be brief, but tell us what features you added and -- crucially -- how we can see them in action.
- Also, by pressing '!', your app switches from "standard" mode to "bonus" mode. This is important because you may want to change some of the behaviors listed above, but you have to match those behaviors to get full credit on the hw. So, the app starts in "standard" mode, and runs exactly as described above. Then, when the user presses '!', the app goes into "bonus" mode, where you can change the behavior as you see fit.