One of my favorite games I played back in my childhood was the snake game. I thought recreating the snake game would be a nice aim for the midterm. However, since I do not want to replicate it but make my own version of it, I am going to get creative with it. I am still not set with the creative part; however, I am thinking of shifting to a new mode after reaching a certain amount of points.
For now, I have almost made the classic snake game. After I figure out all the features I would like to add for my new mode, I will add it as an if statement when the score reaches a certain amount.
https://youtu.be/v1hmxYABemE
Code:
ArrayList<Integer> x = new ArrayList<Integer>(), y = new ArrayList<Integer>();
int w=30, h=30, blocks=20, direction=2, foodX=15, foodY=15, speed = 8, fc1 = 255, fc2 = 255, fc3 = 255; PFont f;
int[]Xdirection={0, 0, 1, -1}, Ydirection={1, -1, 0, 0}; //coordinates for x and y
boolean gameover=false;
void setup() {
size(600, 600);
f = createFont("Georgia", 35);
x.add(0); y.add(15); //snake starting position
}
void draw() {
background(0);
fill(3, 252, 169); //snakes color
for (int i = 0; i < x.size(); i++) rect(x.get(i)*blocks, y.get(i)*blocks, blocks, blocks);
if (!gameover) {
fill(fc1, fc2, fc3); //food color red
ellipse(foodX*blocks+10, foodY*blocks+10, blocks, blocks); //food
textAlign(LEFT); //score
textFont(f);
textSize(25);
fill(255);
text("score : " + x.size(), 30, 30, width - 20, 50);
//makes the snake longer
if (frameCount%speed==0) {
x.add(0, x.get(0) + Xdirection[direction]);
y.add(0, y.get(0) + Ydirection[direction]);
if (x.get(0) < 0 || y.get(0) < 0 || x.get(0) >= w || y.get(0) >= h) gameover = true;
for (int i=1; i<x.size(); i++)
if (x.get(0)==x.get(i)&&y.get(0)==y.get(i)) gameover=true;
if (x.get(0)==foodX && y.get(0)==foodY) { //new food
if (x.size() %5==0 && speed>=2) speed-=1; // every 5 points speed increases
foodX = (int)random(0, w); //new food
foodY = (int)random(0, h);
} else {
x.remove(x.size()-1);
y.remove(y.size()-1);
}
}
} else {
fill(255);
textSize(30);
textFont(f);
textAlign(CENTER);
text("GAME OVER \n Your Score is: "+ x.size() +"\n Press ENTER", width/2, height/3+30);
if (keyCode == ENTER) {
x.clear();
y.clear();
x.add(0);
y.add(15);
direction = 2;
speed = 8;
gameover = false;
}
}
}
void keyPressed() {
int newdir=keyCode == DOWN? 0:(keyCode == UP?1:(keyCode == RIGHT?2:(keyCode == LEFT?3:-1)));
if (newdir != -1) direction = newdir;
}
The idea for my game is to have a player cross a maze in which they will have to avoid a number of enemies. The player will have a limit visibility of the maze (delimited by a circle around the player character). The player will be able, however, to see the position of the enemies (at least that is the idea). It is kind of a PacMan inspired game but with a twist, you could say.
Here is a rough sketch of how the game will look (kind of):
I also very recently came up with the idea to set a theme for the game (I am creating my own sprites so I needed inspiration). Up until now, I have settled for a Harry Potter theme (I have a friend who is obsessed with Harry Potter) because I found these sprites on internet while I was looking for examples and they reminded me of it.
These are the sprites:
The following made me think about the dementors in the series.
Progress:
For this week I decided to focus on creating the maze (more specifically, been able to generate random mazes) and on going through the PGraphics library. I went through the documentation some online tutorials to better understand how to use it.
Note: the fact that I wrote that I decided to focus on these things means that I actually took a long time to understand PGraphics and even more on creating the code to generate the maze.
To create the maze, I decided to create a grid with cells. The program starts at the upper left cell. It checks for neighboring cells and selects a non-visited cell (one the program has not gone through) and removes a wall (line of the grid). Then it moves to that cell and checks for neighbor cells: the process repeats until a path has been formed. Then the program fills in the left out non-visited cells and randomly removes a wall from them to make them look like a maze. The program is recursive so the last position will be that at the beginning of the maze.
The following is a description of my thought process along with the written algorithm (taken from Wikipedia , there are a lot of options to choose from there).
I had a lot of problems writing the code.
At the beginning it was ok. I manage to create a grid and to be able to place in a different color the visited cell.
The problem came when we started to check the neighboring cells. The problem was the edges: sometimes the program will run well and would move to the neighbor cell, however, when the current cell was an edge, sometimes it choose a neighboring cell outside the canvas, so I had to write a case for that.
This is how it would look:
It was also quite hard to figure out how to assign the cells to certain variable (there was a lot of sketching and thinking involved behind that and it took a lot of time to figure out just how to show and remove the walls).
In many cases, what generate was something that did kind of look like a maze but that had no solution.
For example:
The problems where almost always associated with logic with what wall I was telling the program to remove. I watched some YouTube videos regarding maze generation to see how they removed the walls (all of them were in other programming languages, but the pseudocode was all I really needed to start fixing this mess).
After much struggling, yesterday I was finally able to generate a maze.
However in the morning, something happened (I think I might have erased something by accident). My code wasn’t working anymore.
After smashing my head against the wall all morning on my birthday because I couldn’t figure what was wrong, Professor Aaron illuminated me by asking with which function the cell was moving. I had accidentally erased the part in which the cell moved to the next to create a new path, and I completely missed it. Perhaps I’ve been staring at this code for so long that my mind was just assuming that it was there, because I swear, the fact that that part of the code was missing just completely went over my head.
I know have a code that works, 7amdullah!
This is the final result (also, yes, I keep switching colors and size because I wasn’t sure about which would look good, but now I decided for the theme of Harry Potter, I settled for this yellow color. Hopefully I won’t change it again).
Code:
Main Tab
int cols, rows;
int cellSize = 50;
ArrayList<Cell> grid = new ArrayList<Cell>();
Cell currentCell;
ArrayList<Cell> stack = new ArrayList<Cell>();
void setup()
{
size(900, 900);
//fullScreen();
cols = floor(width/cellSize);
rows = floor(height/cellSize);
//create Cell objects and add them to array list
for (int j = 0; j < rows; j++)
{
for (int i = 0; i < cols; i++)
{
Cell newCell = new Cell(i, j);
grid.add(newCell);
}
}
//start the path at grid 0
currentCell = grid.get(0);
}
void draw()
{
background(51);
//check that the grid displays correctly
for (int i = 0; i < grid.size(); i++)
{
//I made the mistake of writing grid.length() instead of grid.size()
grid.get(i).show();
}
currentCell.visited = true;
currentCell.highlight();
//currentCell.checkAdjacentCells();
// STEP 1
Cell nextCell = currentCell.checkAdjacentCells();
if (nextCell != null)
{
nextCell.visited = true;
// STEP 2
stack.add(currentCell);
//STEP 3: remove the walls
removeWalls(currentCell, nextCell);
// STEP 4
currentCell = nextCell;
}
//this is what corrected the problem with the null value
else if (stack.size() > 0)
{
currentCell = stack.remove(stack.size()-1);
}
}
void removeWalls(Cell current, Cell next)
{
int x = current.i - next.i;
if (x == 1)
{
//remove left wall
current.walls[3] = false;
//remove right wall
next.walls[1] = false;
} else if (x == -1)
{
//remove left wall
current.walls[1] = false;
//remove right wall
next.walls[3] = false;
}
int y = current.j - next.j;
if (y == 1)
{
//remove bottom wall
current.walls[0] = false;
//remove top wall
next.walls[2] = false;
} else if (y == -1)
{
//remove top wall
current.walls[2] = false;
//remove bottom wall
next.walls[0] = false;
}
}
Cell Object
class Cell {
int i, j;
//order of the walls is top, right, bottom, left
//this will allow us to remove walls in the grid
boolean[] walls = {true, true, true, true};
boolean visited = false;
Cell(int indexI, int indexJ) {
i = indexI;
j = indexJ;
}
int index(int i, int j)
{
//return 0 for edge cases
if (i < 0 || j < 0 || i > cols-1 || j > rows-1)
{
return 0;
}
//formula to calculate index (same as the one to calculate pixels)
int index = i + j * cols;
return index;
}
Cell checkAdjacentCells() {
ArrayList<Cell> adjacentCells = new ArrayList<Cell>();
//get the cells adjacent to the cell the program is currently at
Cell top = grid.get(index(i, j-1));
Cell right = grid.get(index(i+1, j));
Cell bottom = grid.get(index(i, j+1));
Cell left = grid.get(index(i-1, j));
if (top != null && !top.visited) {
adjacentCells.add(top);
}
if (right != null && !right.visited) {
adjacentCells.add(right);
}
if (bottom != null && !bottom.visited) {
adjacentCells.add(bottom);
}
if (left != null && !left.visited) {
adjacentCells.add(left);
}
if (adjacentCells.size() > 0) {
int r = floor(random(0, adjacentCells.size()));
return adjacentCells.get(r);
} else
{
return null;
}
}
void highlight() {
int x = i*cellSize;
int y = j*cellSize;
noStroke();
fill(0, 255, 0);
rect(x, y, cellSize, cellSize);
}
void show() {
int x = i*cellSize;
int y = j*cellSize;
stroke(255);
//if the walls are defined
if (walls[0])
{
line(x, y, x + cellSize, y);
}
if (walls[1])
{
line(x + cellSize, y, x + cellSize, y + cellSize);
}
if (walls[2])
{
line(x + cellSize, y + cellSize, x, y + cellSize);
}
if (walls[3])
{
line(x, y + cellSize, x, y);
}
//line(x , y , x + cellSize, y);
//line(x + cellSize, y , x + cellSize, y + cellSize);
//line(x + cellSize, y + cellSize, x , y + cellSize);
//line(x , y , x+ cellSize , y);
//noFill();
//rect(x, y, cellSize, cellSize);
//change color of a cell if it has been visited
if (visited)
{
noStroke();
fill(255, 204, 102, 150);
rect(x, y, cellSize, cellSize);
}
}
}
This week we documented the progress we have so far on the midterm assignment.
Idea:
For this assignment, I had several ideas to create a game. First I thought it would be interesting to continue my process from Week 3 on the car game and make it significantly better both aesthetically and technically. Then I came up with the idea of creating a Whack a Mole game. Finally, I decided to recreate my own version of a game I used to enjoy when I was a child. The game was a mini-game from Mario Bros on the Nintendo DS called “Sort or Splode”. The game consisted of two types of bombs appearing on the screen while the user is trying to sort the bombs in platforms according to their colors. If time passes and a bomb is not sorted, it explodes and the game ends. Also if the bomb is sorted into the wrong platform the player loses the game.
*Image from Mariowiki.com (Sort or ‘Splode. Super Mario Wiki. https://www.mariowiki.com/Sort_or_%27Splode.)
Challenges:
I first thought this project was going to be difficult to recreate because I thought I needed a lot of classes and methods to complete it. The use of the mouse and the interaction between the bombs are major concerns I have. One of the main challenges I encountered was randomizing the movement of the bombs. When I first started working on the movement I realized all the circles were moving in the same direction always and I had to spend a good amount of time figuring out how to randomize such movements. Also, another challenge I have encountered which I have not yet figured out how to do is detecting collision between the bombs and changing movement and direction so that bombs do not overlap.
Process:
For this project, I first started organizing the game into classes. I wrote down what classes I needed and the methods and attributes related to each class. This way I only need to fill in the methods and organize the classes to complete the game. Although there is a possibility that I will include more methods and attributes as the project progresses, this gave me a basic idea of where to start on the project. I worked on the creation of the bombs first because I consider it the main class of the game. For this time, I have completed the random movement and the initial part of instantiating a bomb object. The movement of the bombs also changes so that they do not pass into the platform.
Conclusion:
Overall I do not have a lot of progress in the game and I acknowledge there is still a lot of work to be done. Still, I think I have enjoyed the process so far. Organization on the game has been improving and the use of Classes is less challenging at this point compared to other projects. There are still important steps to be completed including the creation of different interfaces for the initial page, instructions, game, game-ending. Also, I need to create the images myself so I further need to work on this part.
int mode = 0; //Determines the mode of the background;
Game game;
Platform platform1, platform2;
void setup(){
size(960, 600);
frameRate(60);
game = new Game();
platform1 = new Platform(1, color(255));
platform2 = new Platform(2, color(75,0,130));
}
void draw(){
background(130);
game.drawGame();
platform1.drawplat();
platform2.drawplat();
class Game{
Bomb bombarray[];
int i;
int velobombs; //Variable determining the velocity of creation of bombs
Game(){
bombarray = new Bomb[100];
i = 0;
velobombs = 50;
}
void createbomb(){
bombarray[i] = new Bomb();
i++;
}
void drawGame(){
if(frameCount%(velobombs) == 0){
createbomb();
}
for(int j=0; j < i; j++){
bombarray[j].drawbomb();
}
}
}
When I was a kid, my siblings and I would play this dot game on the napkins if we were ever in a restaurant waiting for our food. We would draw a grid of dots and draw lines between them, seeing who could form the most “houses”–drawing four lines between the dots to form a box.
For my midterm, I wanted to build a digital version of this game. I will use the Processing Network library to design it for two players.
Here are the requirements and how I play to include them.
At least one shape –> will definitely use dots and lines to form the grids and player drawings
At least one image –> think I will incorporate this into the start screen
At least one sound –> think it would be cool to have a little beep each time a dot is clicked or some sound while the other player is drawing.
At least one on-screen text –> I will have room on the bottom of the screen to indicate in text whose turn it is.
The game must start with a screen giving instructions, and must wait there until a button or key (your choice) is pressed.
After the game is won or lost, there must be a way to restart the game without closing and restarting the program. –> will create a play again button.
Here’s what my grid structure looks like for both the server and client sketches. Not sure if they should have different colored boards.
If I’m being honest, my capstone draft for our community presentation is due the day before this project, so here is what I hope to get out of this project:
Use Network library for the first time
Create a game that is simple but has sentimental value
In regard to my progress, I’ve created the Dot class and implemented drawing things on both the server and client.
For my midterm, I got inspired by one of my favorite childhood games – “Fireboy and Watergirl.” It is an arcade game with two players where characters need to reach the portal avoiding obstacles and collecting diamonds.
I want to draw the sprite sheet for characters myself and make them representatives of other elements. I began the drawing process using Krita software, but it is not ready yet.
For now, I have decided to begin with the instruction screen, game screen, play again screen, and how to navigate through them. For this, I used If statements and the variable “state.” This tutorial was extremely helpful.
I also implemented a timer so how much time left is displayed on the screen. I want to make time left more visually appealing so it won’t be just a text. When time is up, the game is over.
This game is inspired by Google’s famous T-Rex, Run! game where the main objective is to steer clear from all the obstacles. The ‘2020 Saviour’ game follows a similar concept however we have incorporated more elements and features as compared to the original game in order to make it more captivating. 2020 Saviour is an interactive single-player game and the theme is related to the current global issue, the COVID-19 pandemic. Similar to the dinosaur in T-Rex, Run! here we have the main character named Saviour who has the ultimate solution to end the pandemic, the 100% effective vaccine! The main purpose of the game is to deliver the vaccine to the right destination which is the hospital. However, the path towards the hospital is not as easy as it sounds like, because on the way Saviour will encounter numerous amount of coronavirus and infected people who want the vaccination all to themselves, and the player should maneuver Saviour in an effective manner to dodge all the obstacles and while doing so Saviour should collect all the masks on the way in order to protect themselves as well as a move to the next level / win the game.
Main Character: Saviour, controlled by the player
Obstacles: Coronavirus and the infected people
Score increaser: The masks
Level 1 : The game starts with the Saviour running towards the right of the screen by itself. Keyboards of UP and DOWN will control the movement of the savior to jump for the masks that add up to the score(i.e. +10) and to slide from the virus objects that can decrease points(i.e. -10). Masks and Virus objects move the opposite way contrary to the Saviour with speed incrementing when Saviour contacts the virus and if Saviour manages to catch the mask speed decreases, i.e 0.25. Saviour can proceeds to Level 2 once the score is equal to 100. If Savior comes in contact with 3 viruses it loses the game, and the screen will display a message saying “Game Over” and in order to restart the game the player should click the start button with the mouse.
Level 2: The game background darkens into a more creepy apocalyptic setup. Now obstacles would be the coronavirus same as in level 1 in addition to the group of infected people standing as an object that player should jump over. This level doesn’t have a decrease in speed, which means it’s harder to reach the final destination as speed will be incremented similar to the first level. If Saviour passes all the obstacles unaffected she will reach the hospital where the cure(vaccine) is needed to end the global pandemic. Game over, Congrats screen will pop up after this indicating the end of the game. The player can restart the game by mouse clicking the start button.
GAME FEATURES.
Board dimensions: board of the game will hold a window size of 1000pixels in width and 600 pixels in height. The background image will include graphics related to the game’s theme: COVID-19, slowly fading from the light and bright urban landscape in Level1 to dark-toned almost apocalyptic background, finally reaching the last departure, hospital with a respective background image.
The game will include at least 6 classes:
1. The Game class holds all the functionalities of every object displayed on the screen.
2. The Creature class, superclass, will initialize all attributes for the latter classes.
3. The Savior class holds the object for the main character of the game, displayed as a female with cure for covid-19 in her hand. Savior class inherits Creature class’ properties.
4. The Obstacle class holds the object for an infected group of people standing still, which the main character jumps through. Obstacle class won’t inherit Creature class’ properties, since it’s static,non-movable object.
5. The Mask class holds the object for masks that are points for the main character that add up to player’s score. Once the score reaches 100, the character moves to Level2. Mask class inherits Creature class’ properties.
6. The Virus class holds the object of an enemy class that can harm the main character and decreases the player’s points when colliding with Savior. Virus class inherits Creature class’ properties.
For my midterm I decided to try my hand at a racing game. The game will be a top down 2D style game. The car will not move relative to the center of the screen but instead the track will move relative to it. This will make the car look like its moving with focus remaining at the center of the screen. This technique is simulating a chase cam that many other games utilize.
This weeks focus was the game setup and art needed to create the game. Sprite sheets took a lot longer then I expected and all the art I found online needed adaptations to fit my game’s needs. First I started on the cars. I attempted to draw them.. big mistake. I then found a screensaver with all the cars I needed on them so with some resizing and cropping I had a sprite sheet. The same problem came up when trying to get icons for the teams. I found a wallpaper but it needed cropping and manipulation from imperfections. This photo editing took a long time but once I had a sprite sheet properly laid out it was easy to bring it into Processing.
Here is a video of the cars looping through the sprites at first
With media being brought into the game, I needed a menu where a player could select a team. I first displayed just the team and the associated car but decided that was not very intuitive. I then created a menu where a player could use the arrow keys to move across and vertically through the listed teams. I made it clear which one was selected by drawing all of them with a tint, and then the one selected with no tint making it look highlighted. I really liked this effect. I also listed the team name up top and have shown the car on the right.
To finish off the menu I need to play with the typefaces a bit, add a nice background and some music and of course the “play” button.
Next Steps:
With the art in and the cars begging to work I have begun movement. This is tricky as it is all relative. If a car is moving at ‘+10’ in the x direction then the background is actually moving at -10. I also am trying to use smoothing to make the car handle nicer and less jerky.
Here is my code thus far
//======= Global variables ========
int gameMode = 1; //0 is main menu, 1 select your car, 2 running game
Team teams[] = new Team[10];
int teamSelector = 0; //Which team from array is selected
int[][] teamDict = {{7,0,7,0,0},{8,1,8,0,1},{5,2,5,0,2},{4,3,4,0,3},{0,4,0,0,4},{2,5,2,1,0},{1,6,1,1,1},{6,7,6,1,2},{9,8,9,1,3},{3,9,3,1,4}}; //Dictonary containing index of car sprite, associated team image sprite index, team name index, menu row, menu col
//====== Team Selection Menu Variables
PImage teamMenuImg;
void setup() {
fullScreen();
//Set up the team array
//=======================================================================================
PImage[] carSprites;
PImage[] teamSprites;
String[] nameArray = {"McLaren F1 Team",
"Scuderia AlphaTauri Honda",
"Mercedes-AMG Petronas F1 Team",
"ROKiT Williams Racing",
"Haas F1 Team",
"BWT Racing Point F1 Team",
"Scuderia Ferrari",
"Alfa Romeo Racing ORLEN",
"Aston Martin Red Bull Racing",
"Renault F1 Team"
};
//Alfa Romeo, Red Bull, Racing Point, Haas, Mclaren, Mercedes, AlphaTauri, Ferrari, Renault, Williams
//Set sprite sheets
carSprites = getCarSpriteSheet("SpriteSheetCars.png"); //Load the car sprite sheet into sprites
teamSprites = getTeamSpriteSheet("Teams.png"); //Load the team sprite sheet into sprites
//Set teams array with appropiate info
for(int i = 0; i < 10; i++){
Team newTeam = new Team();
newTeam.setName(nameArray[teamDict[i][2]]); //Set team name
newTeam.setTeamImg(carSprites[teamDict[i][0]]); //Set team img
newTeam.setCarImg(teamSprites[teamDict[i][1]]); //Set team car img
teams[i] = newTeam;
}
//=======================================================================================
//Load menu img
teamMenuImg = loadImage("Teams.png");
}
void draw() {
//Main Gamemode Control selector
switch(gameMode){
//Select a car ------------------
case 1:
background(170); //Redraw background
imageMode(CENTER); //Image mode
tint(80); //Darken unselected cars
float menuWidth = (7*width)/10;
float menuHeight = (7*height)/10;
float menuX = (4.5*width)/10;
float menuY = (11*height)/20;
image(teamMenuImg, menuX, menuY, menuWidth, menuHeight); //Menu
tint(255);
float teamImageWidth = menuWidth/5;
float teamImageHeight = menuHeight/2;
float teamImageX = (menuX - menuWidth/2) + (teamDict[teamSelector][4] * teamImageWidth) + (teamImageWidth/2); //Where to draw the overlay team iamge on the menu x and y
float teamImageY = (menuY - menuHeight/2) + (teamDict[teamSelector][3] * teamImageHeight) + (teamImageHeight/2);
image(teams[teamSelector].getCarImg(), teamImageX, teamImageY, teamImageWidth, teamImageHeight); //Draw team
image(teams[teamSelector].getTeamImg(), (9*width)/10, height/2); //Draw car
fill(0);
stroke(0);
textSize(32);
text("Select a team", width/10, height/10);
textSize(64);
text(teams[teamSelector].getName(), width/10, height/10 + 70);
noLoop(); //Save performance
break;
//-------------------------------
}
}
//Keyboard input controller
void keyPressed(){
//Arrow keys during car selection
if(gameMode == 1){
if(keyCode == LEFT){
//Left key selection
int temp = teamSelector - 1;
//Check if on 0 or 5,
if(!(teamSelector == 0 || teamSelector == 5)){
teamSelector = temp;
}
loop();
}
else if(keyCode == RIGHT){
//Right key selection
int temp = teamSelector + 1;
//Check if on 4 or 9,
if(!(teamSelector == 4 || teamSelector == 9)){
teamSelector = temp;
}
loop();
}
else if(keyCode == UP){
//Left up selection
int temp = teamSelector - 5;
//Check if not below 0 to set new value
if(temp >= 0){
teamSelector = temp;
}
loop();
}
else if(keyCode == DOWN){
//Left up selection
int temp = teamSelector + 5;
//Check if not above to set new value
if(temp <= 9){
teamSelector = temp;
}
loop();
}
}
}
//Function which returns the array of sprites from the spritesheet of all the cars
PImage[] getCarSpriteSheet(String fileName){
PImage spritesheet;
PImage[] sprites;
int counter = 0; //Used to sepcify which car to display in sprite list
int w = 151; //Width of one car
int h = 404; //Height of one car
spritesheet = loadImage(fileName);
sprites = new PImage[12]; // 12 images across, 4 down, in the spritesheet
//Loop through 5 rows
for (int i = 0; i < 5; i++) {
//Loop through 2 columns
for (int j = 0; j < 2; j++) {
sprites[counter] = spritesheet.get(j*w, i*h, w, h);
counter++;
}
}
return sprites;
}
//Function which returns the array of sprites from the spritesheet of all the teams
PImage[] getTeamSpriteSheet(String fileName){
PImage spritesheet;
PImage[] sprites;
sprites = new PImage[12]; // 12 images across, 4 down, in the spritesheet
spritesheet = loadImage(fileName);
int counter = 0; //Used to sepcify which car to display in sprite list
int w = spritesheet.width / 5; //Width of one car
int h = spritesheet.height / 2; //Height of one car
//Loop through 2 rows
for (int i = 0; i < 2; i++) {
//Loop through 5 columns
for (int j = 0; j < 5; j++) {
sprites[counter] = spritesheet.get(j*w, i*h, w, h);
counter++;
}
}
return sprites;
}
For the midterm project, I decided to expand on the game I created for the Week 3 assignment. The rules of this game, similar to those of the game I previously created, are pretty straightforward. Attached below is the link to my game for the Week 3 assignment.
The game I am creating is primarily based on collision detection. In the previous version of the game (Week 3), the player played the game as Faiza the Falcon, and had to avoid certain obstructions (referred to as distractions in the game) on the way to obtain a 4.0 GPA.
My midterm project, however, will come with a lot more features. I aim to extend my previous idea in a way where I can create multiple levels (I am aiming for 10 levels), in increasing orders of difficulty. My Week 3 game will only be the final level of my midterm project.
The first (hopefully) 9 levels are based on an entirely different concept, where Faiza has to avoid different obstructions, again, but in order to save her mental health. These obstructions will be things like early morning/late night classes (due to timezone differences), assignments and anxiety. Unlike my previous game where all obstructions behaved in the same way, this version will have all of the obstructions behaving in entirely different ways. For instance, anxiety will constantly be following Faiza, and assignments will be shot at Faiza’s coordinates as they continue to change.
Over the weekend, did a lot of research on how I could implement many of these features. Firstly, I worked on learning how to use inheritance in Java. The previous version of my game had a lot of blocks of code which were repeated, and I learnt how using inheritance could let me create a Creature() class with all the basic attributes and methods, which other classes could inherit from. This made my code a lot simpler and nicer to work with.
Secondly, I worked on getting suitable sprites and backgrounds for different parts of the game, including the introduction screen, the final screen and the main game screen. I also managed to do some research on the implementation of the “following” behavior of some of the obstructions (as I intend to make anxiety follow Faiza), and realized that this had to be done using trigonometric functions (and I’m still in the process of figuring that out).
Overall, I believe this week was more about getting things together in one place and finalizing what the final game should look like. There’s a lot of work to be done in the coming week, but I’m really looking forward to it.
Attached below are the screenshots of some of the things I have managed to come up with.
import java.lang.Math;
import processing.sound.*;
SoundFile file;
String audioName = "intro.mp3";
String path;
Game game;
// creating an array for distractions which are used later
Distractions[] distractions;
class Creature {
float posX, posY, radius, velocityX, velocityY, imgwidth, imgheight;
PImage sprite_image;
int num_frames, frame;
String directionX;
Creature(float x, float y, float r, String image_name, float img_w, float img_h, int number_frames) {
posX = x;
posY = y;
radius = r;
velocityX = 0;
velocityY = 0;
directionX = "right";
sprite_image = loadImage(image_name);
imgwidth = img_w;
imgheight = img_h;
num_frames = number_frames;
frame = 0;
}
void display() {
update();
if (directionX == "right") {
image(sprite_image, float(int(posX - imgwidth/2)), float(int(posY - imgheight/2)), imgwidth, imgheight, int(frame * imgwidth), 0, int((frame + 1) * imgwidth), int(imgheight));
} else if (directionX == "left") {
image(sprite_image, float(int(posX - imgwidth/2)), float(int(posY - imgheight/2)), imgwidth, imgheight, int((frame + 1) * imgwidth), 0, int(frame * imgwidth), int(imgheight));
}
}
void update() {
}
}
class Faiza extends Creature {
boolean move_up, move_down, move_right, move_left;
boolean alive;
int counter;
Faiza(float x, float y, float r, String image_name, float img_w, float img_h, int number_frames) {
super(x, y, r, image_name, img_w, img_h, number_frames);
move_up = false;
move_down = false;
move_right = false;
move_left = false;
alive = true;
counter = 0;
}
void update() {
//The condition below is for when Faiza moves left
if (move_left == true) {
velocityX = -2;
if (posX - radius + velocityX < 6) {
velocityX = 0;
}
posX += velocityX;
}
//The condition below is for when Faiza moves right
else if (move_right == true) {
velocityX = 2;
if (posX + radius + velocityX > 1018) {
velocityX = 0;
}
posX += velocityX;
}
//If none of the left and right keys are being pressed, Faiza stops moving horizontally
else {
velocityX = 0;
}
if (move_up == true) {
velocityY = -2;
if (posY - radius + velocityY <= 5) {
velocityY = 0;
}
posY += velocityY;
}
//The condition below is for when Faiza moves downwards
else if (move_down == true) {
velocityY = 2;
if (posY + radius + velocityY >= 762) {
velocityY = 0;
}
posY += velocityY;
}
//If none of the up and down keys are being pressed, Faiza stops moving vertically
else {
velocityY = 0;
}
if ((frameCount%5 == 0) && (velocityX != 0 || velocityY != 0)) {
frame = (frame + 1) % (num_frames - 1);
} else if (velocityX == 0 && velocityY == 0) {
frame = 8;
}
if (distance(game.gpa) <= (radius + game.gpa.radius) && game.level == 11) {
game.level += 1;
}
if (!(posX >= 0 && posX <= 100 && posY >= 530 && posY <= 640)) {
for (int i = 0; i < 6; i++) {
if (distance(distractions[i]) <= radius + distractions[i].radius) {
counter += 1;
alive = false;
}
}
}
}
// this distance method will be used to check for collisions with distractions
double distance(Distractions target) {
float a = (posX - target.posX);
float b = (posY - target.posY);
double c = Math.pow(a, 2);
double d = Math.pow(b, 2);
return Math.pow(c + d, 0.5);
}
// this distance method will be used to check for collisions with the gpa (marking the end of the game)
double distance(GPA target) {
float a = (posX - target.posX);
float b = (posY - target.posY);
double c = Math.pow(a, 2);
double d = Math.pow(b, 2);
return Math.pow(c + d, 0.5);
}
}
class Distractions extends Creature {
Distractions(float x, float y, float r, String image_name, float img_w, float img_h, int number_frames) {
super(x, y, r, image_name, img_w, img_h, number_frames);
velocityX = random(2, 5);
velocityY = -1 * random(2, 5);
}
void update() {
if (frameCount % 12 == 0) {
frame = (frame + 1) % num_frames;
}
if (posX + radius >= 1024) {
velocityX *= -1;
}
if (posX - radius <= 0) {
velocityX *= - 1;
}
if (posY - radius <= 10) {
velocityY *= -1;
}
if (posY + radius >= 780) {
velocityY *= -1;
}
posX += velocityX;
posY += velocityY;
}
}
class GPA extends Creature {
GPA(float x, float y, float r, String image_name, float img_w, float img_h, int number_frames) {
super(x, y, r, image_name, img_w, img_h, number_frames);
}
}
class Game {
float game_width, game_height;
Faiza faiza;
GPA gpa;
int level;
PImage intro_bg, final_bg, over_bg;
Game(float game_wth, float game_hght) {
level = 0;
game_width = game_wth;
game_height = game_hght;
faiza = new Faiza(34, 585, 27, "faiza.png", 66, 66, 9);
gpa = new GPA(990, 35, 25, "gpa.png", 70, 56, 1);
intro_bg = loadImage("start_background.png");
final_bg = loadImage("background.png");
over_bg = loadImage("gameover_background.png");
distractions = new Distractions[6];
distractions[0] = new Distractions(100, 300, 58, "jake.png", 120, 120, 6);
distractions[1] = new Distractions(444, 333, 48, "insta.png", 100, 100, 1);
distractions[2] = new Distractions(900, 120, 48, "facebook.png", 100, 100, 1);
distractions[3] = new Distractions(887, 635, 48, "netflix.png", 100, 100, 1);
distractions[4] = new Distractions(134, 587, 48, "youtube.png", 100, 100, 1);
distractions[5] = new Distractions(55, 100, 48, "ps.png", 120, 120, 1);
}
void update() {
if (faiza.alive == false) {
faiza.posX = 34;
faiza.posY = 585;
faiza.alive = true;
}
}
void display() {
update();
if (level == 0) {
image(intro_bg, 0, 0);
}
if (level == 11) {
image(final_bg, 0, 0);
}
if (level == 11) {
textMode(CENTER);
textSize(40);
fill(255, 213, 43);
text("GET THAT 4.0!", 310, 65);
}
if (level == 12) {
image(over_bg, 0, 0);
textSize(150);
fill(255, 213, 43);
text("GAME", 270, 220);
text("OVER", 290, 350);
textSize(50);
text(faiza.counter + " distraction(s) later,", 240, 550);
text("you achieved that 4.0 GPA!", 200, 600);
}
if (level > 0 && level <= 11) {
faiza.display();
}
if (level == 11) {
gpa.display();
}
if (level == 11) {
for (int i = 0; i < 6; i++) {
distractions[i].display();
}
}
}
}
void setup() {
size(1024, 768);
game = new Game(1024, 768);
path = sketchPath(audioName);
file = new SoundFile(this, path);
file.loop();
}
void draw() {
background(255, 255, 255);
game.display();
}
// allowing key presses to dictate Faiza's movement
void keyPressed() {
if (keyCode == 32 && game.level == 0) {
game.level = 11;
}
if (key == CODED) {
if (keyCode == RIGHT) {
game.faiza.move_right = true;
}
if (keyCode == LEFT) {
game.faiza.move_left = true;
}
if (keyCode == UP) {
game.faiza.move_up = true;
}
if (keyCode == DOWN) {
game.faiza.move_down = true;
}
}
}
void keyReleased() {
if (key == CODED) {
if (keyCode == RIGHT) {
game.faiza.move_right = false;
}
if (keyCode == LEFT) {
game.faiza.move_left = false;
}
if (keyCode == UP) {
game.faiza.move_up = false;
}
if (keyCode == DOWN) {
game.faiza.move_down = false;
}
}
}
So, we have to make a game for our midterm. The first thing that came to my mind was the long-missed card game I used to play with my family. That game is a 5 player game that would have become very difficult to cod in the limited time and also go beyond the scope of the assignment. Hence, I decided to adapt that game to my version of a 2-player game (somewhat similar to an already existing card game called ‘Honeymoon Bridge’). I get so excited by even just hearing the word ‘cards’, hence there was no better choice for me for this project.
Now to simplify what the game is (as many people aren’t familiar with tricks-making card games), here is an outline –
There will be a normal playing deck of 52 cards, however, only 26 cards will be dealt.
Each player gets 13 cards and the aim of the game is to make the most tricks.
None of the players know which are are still in the deck / have not been dealt so a lot of strategy and some guesswork is required to win the game.
Now, what does it mean to make a trick? Both players play a card one by one. The bigger numbered card gets the trick (King is the highest).
There are a few rules as to what cards you can play. The first player can put any card, but everyone else on the table has to play a card of the same suit. In case you do not have any cards left of that suit, you may play any other suit but you cannot get the trick in that round.
The player who plays first is decided by who makes the trick. For the first round, the person who is dealt first plays first.
Since each player has 13 cards, there will be 13 rounds and any player needs a minimum of 7 tricks to win the game.
Once 7 tricks have been made, the game finishes.
Besides figuring out the logic, I also had to keep in mind how visually my game arena looks and how I envision my cards to move along the screen to give a sense of an actual card game. I started with first planning out and sketching all my idea (see one of my many pages below), going over pseudocode, and then finally started programming it in Processing.
I first made placeholders for where all I wanted my cards, deck, playing table, etc. to be.
Once the cards and deck were placed, it was time to deal cards. I was going for a theme where one could see each card being dealt. Initially, it was a big disaster (see video below). I was unable to control the position or the timing of the cards being dealt. After working for about 3 hours on it, I decided to leave it for a while and then come back to it.
If not physically, I thought it is a good idea to focus on logically dealing the cards. For that, I made 2 arrays – p1Hand and p2Hand – to keep track of card IDs that have been assigned to each player. I used random() to assign the cards and a boolean variable dealt to keep track of if a card has been dealt or not (so as to not repeat a card while dealing). Once the cards had been dealt ‘logically’, I made a sorting function to arrange the cards by suit and number because you cannot play a good card game without having arranged the cards in hand properly! I used Bubble Sort to do this (which I knew from one of my previous CS courses).
After this, I tried again to deal the cards ‘physically’ on the screen and surprisingly succeeded. I got to it by going step by step and building a really complicated code, which I hope to simplify if I am able to think of a better alternative. Though, having the cards deal exactly how I wanted them to, gave me a lot of satisfaction. This was the hardest part till now.
Then I made placeholders for where the cards had to be played on the playing table and just tried coding for the cards to show up there with a click.
Once that worked properly, I tried going further and coding this according to game rules (being able to click on certain cards or not according to the game rules and keeping track of whose turn it is). Also, after both players have played their cards, the round counter increments, and I call the trick() function to check who won the trick and calculate the total number of tricks of each person.
This is my progress as of now (better to watch the video on 2x speed! :D) –
It contains a few bugs that I still need to fix –
The tricks are not incrementing all the time. There is some error in that which I have to look at
Not all cards are being shown on top
Things I still need to work on –
Change the turn based on who makes the trick
After making a trick, the cards need to go on the right side and stack up like you put tricks in a line (this will again require animation)
Rounds need to be updated properly
Appropriate delays need to be made in order to see the movements on the board
Decide whether PC is the second player or have 2 people play it – and accordingly code it
Add sounds of dealing, playing, stacking to make it more realistic
Make the menu and instructions page
Make the end page with a restart option
Introduce the trump variation if time left
Introduce the ‘baazi’ variation if time left
(and anything else that I am currently missing)
CODE
The Card class:
class Card
{
int id; /* rom 1 to 52 -
first 13 = clubs, then diamonds, heart and last 13 spades
- according to heirarchy of suits */
int number;
String suit;
PImage cImg;
float posX, posY, prevPosX, prevPosY;
boolean dealt; //it the card has been assignmed to any player
boolean visible; //a card is visible only in the hand or table
boolean played; //to keep track whether the card has been played by a player or not
//constructor - get parameters of photo, number and suit
Card (int cid, int num, String s, PImage img)
{
id = cid;
number = num;
suit = s;
cImg = img;
dealt = false;
visible = false;
played = false;
}
}
The main code:
/*
SOME BASIC GAME RULES
A deck of 52 cards
Only 26 cards are dealt - 13 per player
Each player plays one card, the one who has a higher card winds the trick - this is one round
the game has 13 rounds
so whoever makes 7 tricks first has more tricks and thus has won the game
the player who plays his card second can only play a card of the same suit unless they don't have that suit
the player who makes the trick plays first in the enxt round
*/
int screenMode = 1;
//loading all images
PImage[] bg;
PImage[] cImgs;
PImage table;
//Deck deck;
Card[] cards;
int[] p1Hand; //keeps track of the cards in hand for player one (by id)
int[] p2Hand; //keeps track of the cards in hand for player two (by id)
//Card[] back; - can have this seperately
//game screen positions
float cardW, deckW, pCardW;
float ratio;
float tableH, tableW; //dimension of the playing table
float marginX, marginY; // to keep track of our=tside table margins
float p1X, p2X; //to keep track of position where players play their cards
float deckPosX, deckPosY;
//variables needed while dealing the cards
boolean dealt = false;
int dealCardNum = 0; //to keep track of which card is being dealt;
float dealIncNum = 5; //how much distance to cover for each dealt card
int[] ctr; //to keep track how much distance my dealt card has travelled
//variables needed while playing
int round = 0; //game consists of total 13 rounds (each player has 13 cards)
int turn = 1; //to keep track which player's turn it is
int firstTurn = 1; //to keep track of which player has to start round - no restriction on playing the card
int card1Played; //to keep track of which card was played first - to match the suit
int p1trick = 0; //to keep track of the number of tricks made by player 1
int p2trick = 0; //to keep track of the number of tricks made by player 2
void setup()
{
//canvas size
size(1200, 800);
//fullScreen();
background(255);
//frameRate(180);
//setting the various positions of game screen
tableW = width*0.6;
tableH = height*0.55;
marginX = (width-tableW)/2;
marginY = (height-tableH)/2;
cardW = (tableW+marginX)/13; //for cards in hand
ratio = 1056/691; //to determine height of cards depending on cardW
deckW = marginX*0.6; //for cards in the deck
p1X = width/2 - deckW;
p2X = width/2 + deckW;
deckPosX = marginX/2;
deckPosY = height/2;
rectMode(CENTER);
imageMode(CENTER);
textAlign(CENTER, CENTER);
//loading backgrounds for different game screens
bg = new PImage[4];
bg[1] = loadImage("bg1.jpg");
//loading additional images
table = loadImage("table.jpg");
//loading card images
cImgs = new PImage[53]; // 1 extra for back side
// to store card image names
String[] filenames = {"back.png", "AC.png", "2C.png", "3C.png", "4C.png", "5C.png", "6C.png",
"7C.png", "8C.png", "9C.png", "10C.png", "JC.png", "QC.png", "KC.png",
"AD.png", "2D.png", "3D.png", "4D.png", "5D.png", "6D.png", "7D.png",
"8D.png", "9D.png", "10D.png", "JD.png", "QD.png", "KD.png",
"AH.png", "2H.png", "3H.png", "4H.png", "5H.png", "6H.png", "7H.png",
"8H.png", "9H.png", "10H.png", "JH.png", "QH.png", "KH.png",
"AS.png", "2S.png", "3S.png", "4S.png", "5S.png", "6S.png", "7S.png",
"8S.png", "9S.png", "10S.png", "JS.png", "QS.png", "KS.png" };
for (int i=0; i<=52; i++)
cImgs[i] = loadImage(filenames[i]);
//creating my card objects
cards = new Card[53];
cards[0] = new Card(0, 0, "back", cImgs[0]);
//for clubs - cards 1-13
for (int i=1; i<=13; i++)
cards[i] = new Card(i, i, "clubs", cImgs[i]);
//for diamonds - cards 14-26
for (int i=14; i<=26; i++)
cards[i] = new Card(i, i-13, "diamonds", cImgs[i]);
//for hearts - cards 27-39
for (int i=27; i<=39; i++)
cards[i] = new Card(i, i-26, "hearts", cImgs[i]);
//for spades - cards 40-52
for (int i=40; i<=52; i++)
cards[i] = new Card(i, i-39, "spades", cImgs[i]);
//player hands
p1Hand = new int[13];
p2Hand = new int[13];
//deal
ctr = new int[26];
for (int i=0; i<26; i++)
ctr[i]=0;
deal();
}
void draw()
{
switch (screenMode)
{
case 0 :
startScreen();
break;
case 1 :
gameScreen();
break;
case 2 :
exitScreen();
break;
case 3 :
instScreen();
break; //instruction screen
}
}
void startScreen()
{
}
void gameScreen()
{
//background - red texture
image(bg[1], width/2, height/2, width, height);
//display the table
pushStyle();
strokeWeight(10);
fill(0);
stroke(0, 120, 70);
rect(width/2, height/2, tableW, tableH);
popStyle();
image(table, width/2, height/2, tableW, tableH);
//displaying deck cards
//for (int i = 0; i<26; i++)
image(cards[0].cImg, marginX/2, height/2, deckW, deckW/691*1056);
pushStyle();
fill(255, 255, 0);
textSize(18);
text("Deck", marginX/2, height/2-deckW/691*1056*1.2/2);
popStyle();
//displaying other placeholders
pushStyle();
fill(0, 0, 0, 160);
rect(p1X, height/2, deckW, deckW/691*1056);
rect(p2X, height/2, deckW, deckW/691*1056);
fill(255, 255, 0);
textSize(16);
text("Player 1 Card", p1X, height/2-deckW/691*1056*1.2/2);
text("Player 2 Card", p2X, height/2-deckW/691*1056*1.2/2);
popStyle();
//dealing cards
if (!dealt)
{
for (int i=0; i<26; i++)
if (ctr[i]>dealIncNum)
if (i%2==0)
image(cards[0].cImg, marginX/2+cardW/2+i/2*cardW, height-marginY/2, cardW, cardW/691*1056);
else
image(cards[0].cImg, marginX/2+cardW/2+i/2*cardW, marginY/2, cardW, cardW/691*1056);
if (dealCardNum%2 == 0)
dealCards(marginX/2+cardW/2+dealCardNum/2*cardW, height-marginY/2);
else
dealCards(marginX/2+cardW/2+dealCardNum/2*cardW, marginY/2);
if (dealCardNum >= 26)
dealt = true;
}
//start game after dealing cards
else
{
//play cards in the center
//println(mouseX + " " + mouseY);
//if it is Player 1's turn
if (turn == 1)
{
if (firstTurn == 1) //player 1 is starting the round
{
if (mouseX<width-marginX/2 && mouseX>marginX/2 &&
mouseY<height-marginY/2+cardW/691*1056/2 && mouseY>height-marginY/2-cardW/691*1056/2
&& mousePressed)
{
//background(255);
int cardPlayed;
cardPlayed = int((mouseX-marginX/2)/cardW);
card1Played = p1Hand[cardPlayed];
cards[card1Played].posX=p1X;
cards[card1Played].posY=height/2;
cards[card1Played].played = true;
turn=2; //player 2's turn now
//image(cards[p1Hand[cardPlayed]].cImg, cards[p1Hand[cardPlayed]].posX, cards[p1Hand[cardPlayed]].posY, cardW, cardW/691*1056);
}
}
}
if (turn == 2)
{
if (firstTurn == 2) //player 2 is starting the round
{
if (mouseX<width-marginX/2 && mouseX>marginX/2 &&
mouseY<marginY/2+cardW/691*1056/2 && mouseY>marginY/2-cardW/691*1056/2
&& mousePressed)
{
//background(255);
int cardPlayed; //gives index of card in hand not cards array
cardPlayed = int((mouseX-marginX/2)/cardW);
card1Played = p2Hand[cardPlayed];
cards[card1Played].posX=p2X;
cards[card1Played].posY=height/2;
cards[card1Played].played = true;
turn=1; //player 2's turn now
//image(cards[p1Hand[cardPlayed]].cImg, cards[p1Hand[cardPlayed]].posX, cards[p1Hand[cardPlayed]].posY, cardW, cardW/691*1056);
}
} else //if the player is second to play in this round
{
if (mouseX<width-marginX/2 && mouseX>marginX/2 &&
mouseY<marginY/2+cardW/691*1056/2 && mouseY>marginY/2-cardW/691*1056/2
&& mousePressed)
{
int cardPlayed; //gives index of card in hand not cards array
cardPlayed = int((mouseX-marginX/2)/cardW);
if (check(p2Hand[cardPlayed], p2Hand)) //to check if the card played is correct or not;
{
//updating position of the card
cards[p2Hand[cardPlayed]].posX=p2X;
cards[p2Hand[cardPlayed]].posY=height/2;
cards[p2Hand[cardPlayed]].played = true;
//see who won the trick
trick(p2Hand[cardPlayed], 2);
round++;
cards[card1Played].visible = false;
cards[p2Hand[cardPlayed]].visible = false;
turn = 1; //needs to be changed according to who made the trick
}
}
}
}
//display all the cards in their respective places
for (int i=1; i<=52; i++)
if (cards[i].visible)
image(cards[i].cImg, cards[i].posX, cards[i].posY, cardW, cardW/691*1056);
pushStyle();
fill(255, 255, 0);
textSize(18);
text(p1trick, width-marginX/2, height/2+deckW/691*1056*1.2/2);
text(p2trick, width-marginX/2, height/2-deckW/691*1056*1.2/2);
popStyle();
//loop();
}
}
void dealCards(float x, float y)
{
if (ctr[dealCardNum]<=dealIncNum)
{
float incrementX = (x-deckPosX)/dealIncNum;
float incrementY = (y-deckPosY)/dealIncNum;
float incrementDeckW = (cardW-deckW)/dealIncNum;
//x = marginX/2+cardW/2+0*cardW;
//y = height-marginY/2;
//dealPosXi += (x - dealPosXi)*0.5;
//dealPosYi += (y - dealPosYi)*0.5;
//dealDeckW += (cardW - dealDeckW)0.5;
image(cards[0].cImg, deckPosX+ctr[dealCardNum]*incrementX,
deckPosY+ctr[dealCardNum]*incrementY,
deckW+ctr[dealCardNum]*incrementDeckW,
(deckW+ctr[dealCardNum]*incrementDeckW)/691*1056);
ctr[dealCardNum]++;
} else
{
//image(cards[0].cImg, x, y, cardW, cardW/691*1056);
dealCardNum++;
}
}
void sortCards(int[] hand) //using bubble sort - I know this from my prev CS course
{
int n = hand.length;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (hand[j] > hand[j+1])
{
//swap the elements
int temp = hand[j];
hand[j] = hand[j+1];
hand[j+1] = temp;
}
}
}
}
boolean check(int index, int[] hand)
{
if (cards[index].suit == cards[card1Played].suit)
return true; //correct card has been played
else
{
//first check if there are cards of this suit
for (int i=0; i<13; i++)
if (cards[hand[i]].suit == cards[card1Played].suit && !cards[hand[i]].played) //found a card of same suit
{
text("You must play a card of the same suit", width/2, marginY*1.1);
return false;
}
return true;
}
}
void trick(int card2, int player)
{
int card1 = card1Played;
if (cards[card1].suit == cards[card2].suit)
{
if (card2 > card1)
{
if (player == 1) //first player played a greater card
p1trick++;
else if (player == 2)
p2trick++;
}
else //if the second card played is smaller
{
if (player == 1) //first player played a greater card
p2trick++;
else if (player == 2)
p1trick++;
}
}
else
{
if (player == 1)
p2trick++;
else
p1trick++;
}
}
void exitScreen()
{
}
//instruction screen
void instScreen()
{
}
void deal()
{
//assigning cards to each player / logically dealing cards
int cid;
for (int i=0; i<13; i++)
{
do {
cid = int(random(52))+1;
} while (cards[cid].dealt);
if (!cards[cid].dealt)
{
cards[cid].dealt = true;
p1Hand[i]=cid; //player 1 has been assigned/dealt this card
//println(cid);
}
do {
cid = int(random(52))+1;
} while (cards[cid].dealt);
if (!cards[cid].dealt)
{
cards[cid].dealt = true;
p2Hand[i]=cid; //player 2 has been assigned/dealt this card
//println(cid);
}
}
//sort the cards in hand - because you cannot play until you arrange cards properly
sortCards(p1Hand);
sortCards(p2Hand);
//displaying hand cards for player 1 that have been assigned/dealt
int j;
for (int i = 0; i<13; i++) {
j = p1Hand[i];
//update position in the class attribute for those cards
cards[j].posX = marginX/2+cardW/2+i*cardW;
cards[j].posY = height-marginY/2;
cards[j].visible = true;
//image(cards[j].cImg, cards[j].posX, cards[j].posY, cardW, cardW/691*1056);
//fill(0,0,255);
//tresting if values have been assignmed properly
//text(p1Hand[i], marginX/2+cardW/2+i*cardW, height-marginY/2);
//text(cards[p1Hand[i]].id, marginX/2+cardW/2+i*cardW, height-marginY/2);
}
//displaying hand cards for player 2 that have been assigned/dealt
for (int i = 0; i<13; i++) {
j = p2Hand[i];
//update position in the class attribute for those cards
cards[j].posX = marginX/2+cardW/2+i*cardW;
cards[j].posY = marginY/2;
cards[j].visible = true;
//image(cards[j].cImg, cards[j].posX, cards[j].posY, cardW, cardW/691*1056);
}
}
For the midterm, coming up with a concrete idea took me quite a while. My initial plan was to continue developing the 2048 game that I made for week 3, but I found that once finished, there is not much room for tweaking the game any further. So I changed my direction and was hoping to use the tile-based premise (like what I used for 2048) and add in character movement (using sprite sheets) and probably a reward system with trading options for the midterm. What I settled on is a game I temporarily name Minehunter.
Idea
As the name suggest, the inspiration for the game comes from the classic game Minesweeper. A quick recap of rules can be found here but I am taking much liberty with it and will not strictly follow these rules. My goal is to use the underlying premise of Minesweeper and add to the user interaction aspect as well as complexity of the game (that is, winning is harder). I plan to implement the following features to achieve this:
Instead of mouse clicking on game cells, the player has an avatar on the game board (the hunter) that they move around using arrow keys. In order to reveal a cell (that the player is sure to be a safe cell) or to plant a flag (that the player is sure to be a mine), the player will have to move their avatar to the specific cell and press corresponding keys.
Once a safe cell is revealed, according to the formal rules, a number is shown representing the number of nearby mines (within the vicinity of 9 cells). I plan to change this so that the number will disappear once the player moves to a different cell, and will only reappear if the player toggles the cell again. This will increase the complexity of the game since the player will need to remember the figures instead of having them being shown permanently on the screen. (This is a loose idea for now, since I’m uncertain on whether it should be a feature.)
My plan is to implement a reward system consisting of points that the player can trade for help. Specifically, the player will start the game with a default of 10 points. For each flag the player manages to plant on a correct mine cell, 5 points are added. There will be a game agent capable of making safe moves (revealing non-mine cells) that the player can trigger, but at the price of having 5 points deducted. This means that if the player has 0 point left, they can’t ask for help from the agent until they successfully flag a mine and earn more points.
Progress
Settling on an idea took me a while, so I have not had much progress with the actual implementation. So far I have had a basic Minehunter class for implementing the game board, which is currently an 8×8 board with 8 mines randomly scattered across the board. The function numNeighborMines() has been written for calculating the number of nearby mines of a particular cell. I also have a basic main program to quickly visualize the game board and test out the functions I’ve written. When running the current program, pressing a random key will reveal the positions of the mines (in darker color) and the number of nearby mines for each of the non-mine cells. So far, the functions seem to run correctly and without errors.
Development
Here is an outline of the main game features that I will need to implement soon in order to build the rest of the game:
The Player class that will display the player’s avatar and allow the player to move around and interact with the game board.
The Reward class that will keep track of the player’s points and adjust according to the player’s actions.
The Agent class capable of making safe moves.
The GameState class for coordinating the game, reseting the game board, etc.
Screenshots & code
Here is a snapshot of my progress so far with initializing the game board:
Minehunter class
class Minehunter {
int numRow = 8;
int numCol = 8;
int numMines = 8;
boolean board[][] = new boolean[numRow][numCol];
ArrayList<String> mines = new ArrayList<String>();
ArrayList<String> minesFound = new ArrayList<String>();
Minehunter() {
// Initializing gameboard with no mine
for (int i=0; i<numRow; i++) {
for (int j=0; j<numCol; j++) {
board[i][j] = false;
}
}
// Formula for encoding coordinates:
// (i, j): i = row coordinate, j = col coordinate
// i, j <= 7 so can encoding in the form 'ij'
// e.g. '56' = (5, 6) = row 5, col 6
// Adding random mines (max 8) to gameboard
while (mines.size() != numMines) {
int i = int(random(numRow));
int j = int(random(numCol));
if (board[i][j] == false) {
board[i][j] = true;
mines.add(tupleToString(i, j));
}
}
}
int numNeighborMines(int row, int col) {
int count = 0;
for (int i=row-1; i<row+2; i++) {
for (int j=col-1; j<col+2; j++) {
if (i==row && j==col) {
continue;
} else {
if (0 <= i && i < numRow && 0 <= j && j < numCol) {
if (board[i][j] == true) {
count += 1;
}
}
}
}
}
return count;
}
boolean isMine(int i, int j) {
return board[i][j];
}
boolean gameWon() {
return minesFound.size() == mines.size();
}
/**
* Print a textual form of the board
* For testing purposes
*/
void printBoard() {
for (int i=0; i<numRow; i++) {
for (int j=0; j<numCol; j++) {
if (board[i][j] == true) {
print("X");
} else {
print("_");
}
}
println();
}
}
String tupleToString(int i, int j) {
return str(i)+str(j);
}
/**
* Coordinate in the form of 'ij'
* i = ij / 10
*/
int stringToX(String coordinate) {
return int(int(coordinate) / 10);
}
int stringToY(String coordinate) {
return int(int(coordinate) % 10);
}
void testMinehunter() {
//println(stringToX("00"), stringToY("00"));
//println(numNeighborMines(4, 4));
}
}