Relaxed Solo Policy
Starting Fri 1-Mar, the solo policy for project1 is relaxed so that you are now allowed to show your running apps to each other, and to discuss design issues with each other. You still are not allowed to show anyone (besides faculty and TAs) your code or to view anyone else's code.
AMG Policy
For those of you who have invested considerable time and effort into project1, but who may not get some of the key features implemented, we are adding a second grading policy to project1. We refer to it as AMG, for Alternative Minimum Grading. We have done this for many years in 112 for these kinds of projects.
AMG grades are capped in the low B range, but they heavily weight time and demonstrated effort (which are not explicitly included in the standard grading, which is results-based rather than effort-based), and they down-weight features that you did not complete.
Also: you do not request AMG grading. Instead, we automatically compute it for everyone. Everyone's project1 grade will be the larger of their standard grade and their AMG grade.
This policy gives everyone who put in consistent effort the security of a pathway to a solid passing grade on the project, even for students who worked hard and produced a lot of good code, but where things just didn't quite come together in the end.
Overview
Deadlines:
Project1 deadline
Sat 2-Mar at 8pm ET
There is no late deadline.
We will not grade anything not submitted by the deadline above.
n/a
Notes:
Be sure to read this entire write-up before starting!
This project is entirely solo except for work done
in lecture and recitation. All other work must be done
by you and you alone.
The only websites you may refer to are CS Academy and Ed.
The only people you may work with are current 112
faculty and staff.
In particular, you may not use any AI tools in any way.
You also may not look at or discuss any code that is not your own
(except for code on CS Academy). The internet is full of code that plays games similar to those assigned here. Do not search for or look at that code, or any tutorials, articles, videos, questions, answers, posts outside of Ed, etc. It won't help anyhow, but in any case, it is not allowed.
Any violation of this is an Academic Integrity Violation.
All your code editing for this project must be done exclusively
in CS Academy, in the specific locations listed below.
Do not use recursion, sets, dicts, or any topics covered
after "Animations with Lists" in the course schedule.
Overview:
Project1 is actually made up of two projects: SuperSet and BlotShooter.
SuperSet is a larger and more technical project that you will write
on your own (though we will provide lots of starter code and hints for you).
BlotShooter is a smaller and less technical project that we will
mostly write together in lecture (so bring your laptops to lecture, since you
will be expected to write code during lecture, in more of a collab format).
Also bring your laptops to Wed recitation so you can engage in
the Style and Design Discussion.
Note:: the sample animations we provide for you are not meant to be matched exactly. You should come close to them, but there is no pixel-perfect autograder. Thus, you are free to vary a bit, so you do not have to exactly match colors or sizes or positions of graphical elements.
For example, you could change the layout of the cards, perhaps making them two rows of 4. Or you could change the layout of the help screen, or add some nice info about the currently-selected theme to the setTheme screen.
The key is: the app should remain close to the sample solution in what it does functionally, even while you have some flexibility in how it does that. So someone who played the sample app could easily play yours and it would "feel" similar -- same screens (though you may add some), same basic gameplay, etc. Plus, you are free to innovate however you wish for your creative elements, so long as basic gameplay still works as expected.
If you are unsure whether a design change would fit within these guidelines, please ask us. We are here to help.
Points:
The project is worth 100 points:
Project Component
Points
Notes
SuperSet
60
Entirely solo.
BlotShooter
35
Mostly collaborative in lecture. The creative elements
are solo.
Style and Design Discussion
5
You will participate in a Style and Design Discussion
in small groups during recitation on Wednesday.
You will be graded only on the quality of your
participation.
Here is the scoring breakdown for SuperSet (these will make more sense
after you read the entire writeup):
SuperSet Component
Points
Pass test functions in console app
15
Display cards in Letters theme
5
Card selection + de-selection
5
Hints feature
5
Timer feature
5
Dimensions (set and use properly with Letters theme)
5
Standard Theme
5
Special Theme
5
Creative Elements (new Themes, better UI, etc)
10
And here is the scoring breakdown for BlotShooter:
BlotShooter Component
Points
Match UI from lecture
25
Creative Elements (you decide!)
10
Also, for especially strong creative work in SuperSet and/or BlotShooter,
we may award up to 5 bonus points max (combined).
Submission
Do all your editing in these three apps in the provided CS Academy locations:
When you are done, and prior to the project1 deadline (Sat 2-Mar at 8pm ET),
you must hit the submit button for each of the 3 files.
Once you hit submit, you will no longer be able to edit your code.
(If you must, you may ask to unsubmit on Ed, but there may be a delay,
we cannot extend the deadline for this, and we will only do this once.)
This is not a homework problem, and thus the grace day period cannot be used.
We will not grade anything not submitted by the deadline.
Important:
To be sure we grade all of your creative work,
you must include a triple-quoted string at the top of each file
that briefly describes any creative and/or bonus work you
did. This way we won't miss it when grading.
Part 1: Learn to play SuperSet
First, carefully watch this video
introducing the game of SuperSet:
Then, take some time to play
this sample solution
of the game. Try some different dims like (3,3), (3,3,3), (3,3,3,3),
and (4,4). Try each of the 3 different themes. Use the hints feature.
Take your time and really explore the game in detail.
Be sure you understand how to play SuperSet before continuing!
Part 2: SuperSet Definitions
Read these definitions and be sure you understand them!
A "card" contains "features" where each feature has several "options"
For example, in the real game of Set, one feature is "color" which
has the options "red", "purple", or "green".
In SuperSet (here), a feature's name is simply its index
(so something like "feature 0" rather than a more meaningful name
like "color"), and its options are just letters like 'A' or 'B'.
To be more precise, if a feature has 3 options, then those options
will be 'A', 'B', or 'C'. For this reason, there is a hard limit
of 26 options per feature, though practically we set that limit much
lower (like 5).
Thus, for example, a card with three features may be represented by
the value 'ABB' where "feature 0" is 'A', and both "feature 1" and
"feature 2" are 'B'.
"cards" is simply a list of cards.
A "deck" is a list of all possible cards.
A "set" is a list of cards where, for every feature, every card either
has the same option or every card has a different option.
A "board" is a list of cards dealt from the deck and shown to the user.
A "selection" is a list of cards selected by the user from those on the board.
In the graphical app, the borders of the cards in the selection are highlighted
in yellow if there are not enough selected cards to form a set.
If there are enough selected cards to form a set, then they are
highlighted in lightGreen if they in fact do form
a set, and in red if they do not form a set.
The "dimensions", or "dims" for short, is a list of the number of
options for each feature.
For example, if the dims are [3, 4], then there are two features:
feature0 with 3 options ('A', 'B', or 'C') and
feature1 with 4 options ('A', 'B', 'C', or 'D')
The "cardsPerSet" is the required integer number of cards to form a set.
In SuperSet, this is precisely the min of all the dimensions.
For example, if the dims are [3, 4], then cardsPerSet is 3.
Note that if cardsPerSet was larger than the smallest dimension,
then a set could never be formed (think about that..).
More precisely: cardsPerSet = min(dims)
In particular, do not do this: cardsPerSet = len(dims) # Wrong!!!
Part 3: SuperSet Helper Functions in Console App
We are getting closer to where you will start building your SuperSet game!
You will start by writing helper functions in the console app.
This way, you can deal with those without the added complexity
of the animation.
Before you get started, though, let's discuss a top-down design
of these functions. This will help you understand why each
function exists. Note that the functions have more complete descriptions
in the header comments in the starter code. Here we just give
a high-level description to help you understand how everything
fits together.
There are 3 top-level functions your app will require.
isSet(cards)
Checks if a list of cards form a legal set.
checkSelectionIsSet(board, selection, cardsPerSet)
Checks that the selected (highlighted) cards on the
board actually form a set (and if not, why not).
getRandomBoardWithSet(dims, targetBoardSize)
Creates a new random board that is guaranteed to
contain a set. It returns both the board and that set.
Here are those same functions, along with the helper functions they
require nested beneath them, again top-down:
isSet(cards)
Described above.
allSame(L)
Confirms that all values in the list L are the same.
allDiffer(L)
Confirms that all values in the list L are different.
That is, that no two values in L are the same.
checkSelectionIsSet(board, selection, cardsPerSet)
Described above.
boardContainsSelection(board, selection)
Confirms that every card in the selection is in fact
on the board.
allDiffer(L)
Defined above, also used here.
isSet(cards)
Defined above, also used here.
getRandomBoardWithSet(dims, targetBoardSize)
Described above.
makeSuperSetDeck(dims)
Creates a new deck (list of cards) that contains
all the possible cards with the given dimensions.
stringProduct(featureStrings)
You do not write this -- we already wrote it for
you.
Given a list of strings, like ['AB', 'CDE'],
this returns a list of the product of those
strings. That is, all the strings formed by one
letter from each of the strings. In this example,
it would return ['AC', 'AD', 'AE', 'BC', 'BD', 'BE'].
See the comment in the code for more details.
dealUntilSetExists(deck, cardsPerSet)
Keeps dealing from a deck until a set of cards is dealt.
We do this to guarantee that the cards on the board
actually contain a set.
findFirstSet(board, cardsPerSet)
Looks at every possible combination of cards
on the board and checks if they form a set.
Returns the first set it finds.
combinations(L, n)
You do not write this -- we already wrote it for
you.
Given a list L and an integer n, this
returns a list of all the combinations of
n values in L. For example, for a list
['A', 'B', 'C'], if n is 2, the function
would return all the ways that we can combine
2 values from the list. That is:
[['A', 'B'], ['A', 'C'], ['B', 'C]].
See the comment in the code
for more details.
While we design the app top-down, we actually write the
functions in reverse order, bottom-up, so that the helper
functions are already written and tested for each
new function that we write.
The functions are listed in
the starter file in this order for you.
Note: while you do have to pass all the test functions in
the console app, you technically do not have to strictly follow our
top-down design. That is, you could write some of the functions above
without using the helper functions we mentioned, and perhaps with using
some other helper functions we did not mention. However, you do still
have to pass all the test functions, even for helper functions we specify
but you ultimately do not use.
With that, you should be ready to write these functions. Once again,
write them in the console app. When you pass the test functions
supplied in that app, proceed to the next step. Good luck!
Hints:
Here are some hints that you may wish
to read and to keep in mind when writing your console app:
Read the header comments for each function very carefully first!
Also, carefully review all the test cases in the provided test function for
each function before you write it!
Do not name any of your variables set. That is a builtin
function in Python, which may lead to confusion.
We have provided two important helper functions for you:
stringProduct and combinations.
These are at the top of the file. Read the header comments
for these functions carefully. You will need to use
these functions in your solution!
Part 4: SuperSet Game in Console App
Note: there is no code for you to write in this part.
The starter code includes a text-based version of the game in the
console app. This is only for debugging purposes, and to be extra
sure that your helper functions work properly before you start writing
the animation.
The text-based game is not meant to be for users, just for you.
As such, it does not have a great user interface. Just enough for
you to play it. Run your code and play the text-based game.
You can press 'h' for a 'hint', but it's not a real hint for users.
It just prints a set (as a string of cards, which are letters).
You could copy-paste that hint string into the game to enter
a set. Try it!
You can also press 'a' to autoplay, where the game will simply
enter the hint string directly for you. This lets you test quickly.
If you wish, you could also just play the game normally, entering
the sets you can find. Give it a go!
Part 5: SuperSet Game in GUI App
You are almost ready to write the animation! Another name for an animation
is a GUI (which stands for "Graphical User Interface").
As a first step, run the starter code for the GUI. This includes the
code you need to write an app that uses multiple screens.
Also, carefully watch
this video that briefly explains how apps with multiple screens
work in CMU Graphics:
Next, you should copy-paste your helper functions from your console
app into the animation, near the bottom. We have placed a comment
in the code where you should paste it.
With that, you are ready to start writing the animation.
Good luck!
Hints:
Here are some hints that you may wish
to read and to keep in mind when writing your GUI app:
Important: due to a bug in CMU Graphics, always place
the cmu_graphics import first. In particular, be sure
to import random after you import cmu_graphics. Otherwise,
you will get strange errors. So your file should start with something like this:
from cmu_graphics import * # make this your first import!
import random # this must come after importing cmu_graphics!
You can draw a shape with a dashed border like so: drawCircle(200, 200, 50, dashes=True)
You can rotate a shape with rotate like so: drawRectangle(200, 200, 50, 50, rotateAngle=45)
You can draw a shape so that it is partly transparent by changing
its opacity, like so: drawCircle(200, 200, 50, opacity=30)
Opacity is the opposite of transparency, so
an opacity of 100 is not at all transparent, and an
opacity of 0 is completely transparent.
You can draw a regular polygon like a triangle, square, pentagon,
etc, like so: drawRegularPolygon(cx, cy, r, points)
Here, points is the number of points (or vertices)
in the regular polygon (so 5 is a pentagon).
You can draw an arbitrary polygon by specifying the list of x,y
points to connect in order, like so: drawPolygon(x0, y0, x1, y1, x2, y2, x3, y3)
You do not have to end with the same point you started with --
cmu_graphics will close the polygon for you.
You can draw left-aligned text like so: drawLabel('Amazing', 200, 200, align='left')
We suggest that you write a function like
getCardBounds(app, i) that returns the
bounds for the ith card on the screen. You can use this
to help draw that card, but also to help figure out
which card was pressed in onMousePress.
We also suggest that you write theme-specific drawCard functions.
Then, your top-level drawCard function will check what the
current theme is and just call the appropriate
theme-specific drawCard function. For example, here
is one such function from our sample solution (which you are free
to use, or not, as you prefer):
Some themes can only handle up to certain maximum dimensions.
For larger dimensions that the theme cannot handle, just default
back to the Letters theme, which can handle arbitrary dimensions.
For this, we found it helpful to write this function: dimsTooLargeForTheme(dims, themeDims)
This takes the current dimensions and the maximum theme dimensions,
and returns True if the current dimenions are too large for
the current theme. In that case, we default to the Letters theme.
When drawing a theme, you will need to get theme-specific options
from a card. Say a card is 'ACB' and you are in the Standard theme.
You need a way to convert 'A' to, say, a color, and 'C' to a shape,
and 'B' to a number (though you can order your features any way you
wish). For that, we found it easiest to first convert 'ACB' into a
list of offsets from 'A' -- so, [0, 2, 1]. We then used these as
indexes into lists holding the properties we need for the theme
(such as a list of colors, or a list of shapes, or a list of numbers).
While getRandomBoardWithSet returns a foundSet along with the board,
we found it helpful to convert the foundSet into a list of indexes
to those cards on the board. We stored these in app.hintIndexes.
So the ith card on the board is in the hint if (i in app.hintIndexes)
is True. Again, you can safely ignore this hint if you wish.
Part 6: BlotShooter Game in GUI App
As noted, we will write We wrote the BlotShooter game together in lecture.
Do not worry about this prior to lecture. Be sure to bring your
laptop to lecture, since you will write the code along with us
in a collab fashion. The goal is for you to leave lecture
with a fully working version of BlotShooter (worth 25 points on the project!),
except for the creative elements that you will add afterwards.
Click this link for a playable version of BlotShooter!
Important: If you cannot attend for a valid, excused reason, you must fill out the excused absence form explained in the syllabus for access to the necessary resources to complete BlotShooter.
If your request is not accepted, you will be expected to be in lecture, so make your request now.
Part 7: Style and Design Discussion (in Wed recitation)
Note: there is no code for you to write in this part.
Take your laptop to Wed recitation. There, the TAs will split you up
into small groups. You will show your code to the others in your group,
and they will show you their code. You are not allowed to make any
edits to your code at this time. Instead, you and your group will
review each others' code for style. You will also discuss design
issues, and any other issues or concerns you may have about how to
complete your project. Again, no editing of code is allowed during
recitation. You will be graded only on the quality of your participation.
After recitation, you are expected to respond to the helpful feedback
you received, and to sharpen up your code accordingly.
Note: for a code review and design discussion to go well, everyone has
to be courteous and respectful. Be open to the helpful feedback from
others, and be sure to offer them helpful, positive feedback on their
code and their design.
Part 8: Creative Elements
Both SuperSet and BlotShooter require that you add some "creative elements".
These have to be original designs of your own. For SuperSet, this must
include one more theme. Ideally, it would be fun and attractive, but it
must also still be playable. We hope you find a way to use animation,
such as the "Special" theme does, so the cards move in some interesting way,
but this is not strictly required.
Beyond that, you are free to innovate however you wish. Just be sure not
to break the basic game as you add your creative features. Also, be sure
to include a triple-quoted string at the top of each file telling us
what your creative elements are, and briefly explaining how we can
trigger those features if it's not obvious (otherwise, we may be unable
to see them ourselves as we use your app to grade it).
To set expectations, since creative elements are together worth 20% of the total project1, we would expect you to spend around 20% of your project1 time on these. That said, we will relax this just a bit, so we would accept 1.5 to 2 hours of devoted effort on this part of the project, and we will grade accordingly.
Part 9: Bonus
While bonus is not required (of course), we may assign a few bonus
points for especially clever, well-designed, engaging creative elements.
Part 10: Looking ahead to project2
One last note: the purpose of project1 is to give you the chance
to create a larger, more "real", hopefully more satisfying app
than we could assign for homework. We have provided a lot
of scaffolding for you: choosing the project, designing the basic
GUI, top-down designing the helper functions, providing test
functions for those helper functions, etc. This gives you your
best chance to create a working app of this complexity in one
week's time. And we have left room for some creative work on
your part as well!
That's project1. The goal for project2 at the end of the course is
for you to make a project of your own choosing (for the most part).
After spring break, you will have time to ideate for that project,
thinking about what you want to create. We will also provide some
options for you, if you would rather skip the ideation process
and do something more prescripted for you. But our hopes are that
by doing a scaffolded "real" project now, you will be ready to
branch out on your own and make something new and wonderful
that is your own creation in project2.
Also, this: to give you the best chance at success on project2,
you need to do reasonably well on project1 first. We will review
your project2 plan, taking into account how project1 went for you.
We must approve your project2 plan before you start working on it.
So for now, focus on making a solid project1.
That's it! Be creative and have fun!
PS: Here are even a few more fun hints for creativity:
Some functions take a series of parameters like the x,y
points in drawPolygon() and the colors in gradient(). What
if you wanted to use a list of those values? To do that,
you need to use the * operator to "unpack" that list into
individual arguments. Here is an example that uses * for a list of colors in a gradient:
# Demo using * to unpack a list of colors in a call to gradient()
from cmu_graphics import *
import random
def onAppStart(app):
app.colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']
app.stepsPerSecond = 2
def onStep(app):
random.shuffle(app.colors)
def redrawAll(app):
fill = gradient(*app.colors) # use * here to unpack list of colors
# into individual arguments to gradient()
drawCircle(200, 200, 200, fill=fill)
def main():
runApp()
main()
A sprite is a series of images that you can show in succession
that appear as an animation. You can make these images yourself,
or you can find them online. Just note that loading lots
of images will slow down your app a lot, so use this
sparingly, if you use it. But it can make some fun
effects. For example, here is a sprite of a person
running:
# Sprite demo
# This repeatedly shows these images in turn to simulate a person running:
# https://www.cs.cmu.edu/~112-s24/notes/images/sprinter-sprite/sprinter0.png
# https://www.cs.cmu.edu/~112-s24/notes/images/sprinter-sprite/sprinter1.png
# https://www.cs.cmu.edu/~112-s24/notes/images/sprinter-sprite/sprinter2.png
# https://www.cs.cmu.edu/~112-s24/notes/images/sprinter-sprite/sprinter3.png
# https://www.cs.cmu.edu/~112-s24/notes/images/sprinter-sprite/sprinter4.png
# https://www.cs.cmu.edu/~112-s24/notes/images/sprinter-sprite/sprinter5.png
from cmu_graphics import *
def onAppStart(app):
baseUrl = 'https://www.cs.cmu.edu/~112-s24/notes/images/sprinter-sprite'
app.imageUrls = [f'{baseUrl}/sprinter{i}.png' for i in range(6)]
app.stepsPerSecond = 7
app.imageIndex = 0
def onStep(app):
app.imageIndex = (app.imageIndex + 1) % len(app.imageUrls)
def onKeyPress(app, key):
if key == 'up': app.stepsPerSecond += 3
elif key == 'down' and app.stepsPerSecond > 3: app.stepsPerSecond -= 3
def redrawAll(app):
# draw the title and instructions:
drawLabel('Sprite Demo', 200, 20, size=16)
drawLabel('Press up/down to go faster/slower', 200, 40, size=16)
# draw the sprite sprinter:
drawImage(app.imageUrls[app.imageIndex], 200, 200, align='center')
def main():
runApp()
main()