Hi, I'm currenly programming a 2048 game and I'm struggling to figure out why I get an error each time 2 blocks Merge "java.lang.IllegalStateException: Actor has been removed from the world."
I'm also not sure why when 2 blocks merge, the new merged block ends up in another location. I think when I assign the x & y positions of a new object it may be random:
GameSquare:
GameBoard
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) import java.util.List; /** * Write a description of class GameSquare here. * * @author (your name) * @version (a version number or a date) */ public class GameSquare extends Actor { //Instance Constants /* Note: * Since these variables are definied at STATIC they will never change (aka are constant). * Normally when we have constant variables like this we name them in ALL CAPS */ //Define some directions to make it easier to reference which way the blocks are moving private static final int UP = 0 ; private static final int RIGHT = 1 ; private static final int DOWN = 2 ; private static final int LEFT = 3 ; //Define a debugging variable (See video linked in assignment outline) private final boolean debug = false ; //Instance Variables private int value; //Constructor public GameSquare() { this ( 2 ); displayValue(); } public GameSquare( int valueIn) { this .setValue(valueIn); displayValue(); } /** * Tell the block to move in the given direction until it hits an obstacle * * @param The direction in which the block is to move (UP = 0; RIGHT = 1; DOWN = 2; LEFT = 3; */ public void move( int direction) { //check if can move int movable = canMove(direction); //if moveable, start a loop while (movable > 0 ) { //Get current coordinates int xPos = getX(); int yPos = getY(); //Change x and y values to the "new" location based on direction if (direction == 0 ){ yPos -= 1 ; } else if (direction == 1 ){ xPos += 1 ; } else if (direction == 2 ){ yPos += 1 ; } else if (direction == 3 ){ xPos -= 1 ; } //If Nothing in the way - move the block if (movable == 1 ) { setLocation(xPos,yPos); movable = canMove(direction); continue ; //return; //Why do we continue here, instead of returning? } //Merge the blocks else { //Find which block we need to merge with if (!getWorld().getObjectsAt(xPos, yPos, GameSquare. class ).isEmpty()) { // check world for spaces with blocks GameSquare block = (GameSquare)getWorld().getObjectsAt(xPos, yPos, GameSquare. class ).get( 0 ); // get objects in the gameSquare world if (merge(block.getValue())) { //Tell the other block object we wish to merge with it. getWorld().removeObject(block); // remove block object getWorld().addObject( new GameSquare(getValue()), yPos, xPos); // set location of new merged object getWorld().removeObject( this ); movable = canMove(direction); continue ; } else { movable = 0 ; // if block cannot move/merge past other object continue ; } } else { movable = canMove(direction); // continue movement if no merging possible continue ; } } } //can't move, so don't move. return ; } /** * Sets the value of the game square to the value of the input parameter. * Will only work if the value is a factor of 2 * * @param The number to use as the new value * @return If the number was set correctly return true, otherwise return false */ public boolean setValue( int valueIn) { if (valueIn % 2 != 0 ){ //modulo to check if num is a factor of 2 return false ; } else { value = valueIn; return true ; } } /** * Merge with another block and absorb it's value. * Will only work if the two blocks are of the same value * * @param The value of the block to be added * * @return Return true if the merge is successful. */ public boolean merge( int valueIn) { if (getValue() == valueIn){ // if objects are of equal value setValue((getValue() * 2 )); //multiply the block value by 2 return true ; } else { return false ; } } /** * Returns the current value of the gameSquare * * @return The current value (int) of the game square */ public int getValue() { return this .value; } /** * Checks to see if the block can move * * @return int value representing what is in the space to be moved to. 0 -> Path Blocked, 1 -> Empty Space, int>1 value of block in the space to be moved to. */ private int canMove( int direction) { //Get World World world = getWorld(); //Get x and y values of current object int xPos = getX(); int yPos = getY(); //Change x and y values to the "new" location based on direction if (direction == 0 ){ yPos -= 1 ; } else if (direction == 1 ){ xPos += 1 ; } else if (direction == 2 ){ yPos += 1 ; } else if (direction == 3 ){ xPos -= 1 ; } //Test for outside border if (xPos < 0 || xPos > 3 || yPos< 0 || yPos > 3 ) { // no less than first index to 4 (index 3) for X & Y pos return 0 ; } //Check to see if there is a block in the way if (!world.getObjectsAt(xPos, yPos, GameSquare. class ).isEmpty()) { // check world for spaces with blocks GameSquare block = (GameSquare)world.getObjectsAt(xPos, yPos, GameSquare. class ).get( 0 ); return block.getValue(); } return 1 ; } /** * displayValue - Displays the current value of a block in an image, then sets that image to the block display image */ private void displayValue() { //Create an image consisting of the display value using built in greenfoot commands GreenfootImage displayImage; displayImage = new GreenfootImage( Integer.toString(value), 20 , Color.BLACK, Color.WHITE); //Add the image as the new image for this object setImage(displayImage); if ( this .value== 2048 )Greenfoot.stop(); //ends scenario once 2048 is reached } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo) import java.util.List; /** * This is a simple game where you join together boxes of the same value and try to get to 2048 * * This assignment is to get you familar with workin in an OO environment and * to get you used to accessing and using java documentation. Below are two * useful links that will help you. * * Greenfoot Javadoc: https://www.greenfoot.org/files/javadoc/ * Java 11 List Javadoc : https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html * * I also talk about access control in the video and you might find this page * interesting to check out if you want to know more about different java * * Extra Videos: See the assignment for some extra video lessons that might help * * @author C. Brooks-Prenger, X. XXXXXX * @version v1.0 - Template */ public class GameBoard extends World { //Instance Constants /* Note: * Since these variables are definied at STATIC they will never change (aka are constant). * Normally when we have constant variables like this we name them in ALL CAPS */ //Define some directions to make it easier to reference which way the blocks are moving private static final int UP = 0 ; private static final int RIGHT = 1 ; private static final int DOWN = 2 ; private static final int LEFT = 3 ; static int blockCount = 0 ; /** * Constructor for objects of class MyWorld. * */ public GameBoard() { // Create a new world with 600x400 cells with a cell size of 50x50 pixels. super ( 4 , 4 , 50 ); //populate gameboard with some randomly placed objects while (blockCount< 1 ) { if (placeRandomBlock()){ blockCount++; } else { continue ; } } } /** * Place a block on a random location on the board * * @return Returns true if successful, false if not successful */ public boolean placeRandomBlock() { //Generate Random Location (Hint: Is there anything to do with random values in the apis?) GameSquare gs = new GameSquare(); //create a new game space int x = (Greenfoot.getRandomNumber( 4 )); //This method returns a random number between 0 and 3 in the x axis. int y = (Greenfoot.getRandomNumber( 4 )); //Same method for y axis (4 x 4 grid) //Check to ensure random location is not yet taken, if the spot is free add it to the world //(Hint: Is there any way to figure out if an object is somewhere in the world in the apis?) if (getObjectsAt(x, y, GameSquare. class ).isEmpty()) { //The isEmpty() method checks whether a string is empty or not. addObject(gs, x, y); // adds object return true ; } else { return false ; } } /** * Act - Check for key presses and tell each block to move itsself. */ public void act() { //Add key press actions here String key = Greenfoot.getKey(); //If a key was pressed...do something if (key != null ) { //Note: you should disable this, but I wanted to show how you can debug in greenfoot System.out.println(key); switch (key) { case "up" : //Tell the blocks to move up //Start checking from the top, then move downwards for ( int i = 0 ; i< getWidth(); i++) { for ( int j= 0 ; j<getHeight(); j++) { //Get a list containing all of the GameSquare objects at position (i,j) List blockList = getObjectsAt(i,j,GameSquare. class ); //Move the block in the direction needed if (blockList.size() == 1 ) { //Error checking - Might want to deal with the case where this isn't true (ie, something went very wrong) //Create a temporary holding space for a generic object Object tempObject; //Get the first (and only) entry in the list tempObject = blockList.get( 0 ); //Create a temporary holding space for the gameSquare object GameSquare tempSquare; //Convert it from the generic "Object" to a GameSquare Object tempSquare = (GameSquare)tempObject; //Then move UP. tempSquare.move(UP); //The above few lines of code is NOT how I would normally write this. //You could accomplish all of the ablove using the single line of below //It can be a bit confusing when code is all in one line. Is this considered good form or bad form? //( (GameSquare)( blockList.get(0) )).move(UP); } } } break ; //NOTE: The remaining cases are similar to the one above, but not exactly the same case "right" : //Tell the blocks to move right //Start checking from the right most col, then move left for ( int i = 3 ; i >= 0 ; i--) { // iterate from right for ( int j = 0 ; j < getHeight(); j++) { List blockList = getObjectsAt(i, j, GameSquare. class ); if (blockList.size() == 1 ) { //move RIGHT. ((GameSquare)(blockList.get( 0 ))).move(RIGHT); } } } break ; case "down" : //Tell the blocks to move down //Start checking from the bottom, then move up for ( int i = 3 ; i >= 0 ; i--) { for ( int j = 3 ; j >= 0 ; j--) { List blockList = getObjectsAt(i, j, GameSquare. class ); if (blockList.size() == 1 ) { // move DOWN ((GameSquare)(blockList.get( 0 ))).move(DOWN); } } } break ; case "left" : //Tell the blocks to move left //Start checking from the left, then move right for ( int i = 0 ; i <= 3 ; i++) { //iterate through x-axis for ( int j = 0 ; j < getHeight(); j++) { List blockList = getObjectsAt(i, j, GameSquare. class ); if (blockList.size() == 1 ) { // move LEFT ((GameSquare)(blockList.get( 0 ))).move(LEFT); } } } break ; } //Since placeRandomBlock is not guaranteed to work the first time, repeat the process until it does (Video) //This is NOT a great way to do this. (Hint: Making this part better is a part of level 4) int count = 0 ; if (numberOfObjects() < 16 ) { // numberOfObjects method fetches the number of obj currently in the world while (count < 1 ) { if (placeRandomBlock()) { count++; } } } } } } |