GAMES TWO: LESSON Fifteen: Searching For Scoring Patterns

Scoring the game of CANTRIS requires that the applet keep track of the values of the individual cells used in the game. So, instead of having the CAN as the smallest unit in the game, the CELL is now the smallest unit in the game. In fact, we no longer use the CAN class at all (although we do have an array named can which serves as a temporary holder of the single active can).

Try the game out. Note that the down arrow now produces a rotation of the cells in the active can.

There are three classes involved in this game: 1) CC4 (the applet class), 2) GRID2, and 3) CELL.

import java.awt.*; class CELL{ int x, y; Color c; CELL(int dd, Color cc){ y = 20-dd; x = 60; c = cc; } public Color getColor(){ return c; } public void setColor(Color cc){ c = cc; } public void moveX(int n){ if(x>30 && n == -1){ x-=10; } else if(x<80 && n == 1){ x+=10; } } public void moveY(){ y+=2; } public void draw(Graphics g){ g.setColor(c); g.fillRect(x,y,10,10); } }
CELL contains the basic methods needed to move the cell and change its color. CELL is used exclusively by GRID2.

GRID2 contains all the methods needed to deal with the elements displayed within the grid area of the game. There are two particularly important methods: scoreGrid and hit_bottom. As the name suggests, scoreGrid searches for scoring patterns. It is called each time a can hits the bottom of its current column. The method hit_bottom transfers the contents of the can array (which represents the active can) to the grid (which keeps track of each individual cell). It is called each time a can hits the bottom of its current column.

import java.awt.*; import java.util.*; public class GRID2{ CELL[] cells = new CELL[120]; CELL[] can = new CELL[3]; int[] grid = new int[300]; int col=3; int bottom[] = {210,210,210,210,210,210}; Color sel[] = { Color.red, Color.blue, Color.cyan, Color.green, Color.magenta, Color.yellow }; int next=0; Random r = new Random(); int cnt, lastCHKD, score; boolean gameOn = true; GRID2(){ for(int i = 0; i<grid.length; i++){ grid[i]=-1; } spawn(); } public void moveX(int m){ if(col>0 && m==-1){ if(test_bottom(m)){ col--; can[0].moveX(m); can[1].moveX(m); can[2].moveX(m); } } else if(col<5 && m==1){ if(test_bottom(m)){ col++; can[0].moveX(m); can[1].moveX(m); can[2].moveX(m); } } } public void rotate(){ Color hold = can[0].getColor(); can[0].setColor(can[1].getColor()); can[1].setColor(can[2].getColor()); can[2].setColor(hold); } public void settle_cells(){ //for future use } public boolean check_gaps(){ //for future use return false; } public void move_can(){ can[0].moveY(); can[1].moveY(); can[2].moveY(); } public void scoreGrid(){ for(int i=0; i<grid.length-3; i++){ //System.out.println("VAL: " + grid[i]); if(i%6<4 && grid[i]!=-1 && grid[i+1]!=-1 && grid[i+2]!=-1){ if(!cells[grid[i]].getColor().equals(Color.black) && cells[grid[i]].getColor().equals(cells[grid[i+1]].getColor()) && cells[grid[i+1]].getColor().equals(cells[grid[i+2]].getColor()) ){ score++; System.out.println("MATCH: " + i + ", SCORE: " + score); cells[grid[i]].setColor(Color.black); cells[grid[i+1]].setColor(Color.black); cells[grid[i+2]].setColor(Color.black); } } } //System.out.println("-----scoreGrid------"); } public boolean keepPlaying(){ if(cnt>cells.length-3) return false; return gameOn; } public void spawn(){ if(next<cells.length-3){ Color c[] = new Color[3]; col = 3; int x = 0; do{ for(int i = 0; i<c.length; i++){ x = Math.abs(r.nextInt()%sel.length); c[i]=sel[x]; } }while(c[0].equals(c[1]) || c[1].equals(c[2]) || c[2].equals(c[0]) ); can[0] = new CELL( 0,c[0]); can[1] = new CELL(10,c[1]); can[2] = new CELL(20,c[2]); } cnt++; } public boolean hit_bottom(){ if(next>=cells.length-4){ gameOn=false; return true; } if(can[0].y >= bottom[col]){ for(int i=0; i<3; i++){ int grid_index=col+(210-bottom[col])*6/10+i*6; //System.out.println("GI: " + grid_index); grid[grid_index]=next; cells[next]=can[i]; next++; } bottom[col]-=30; return true; } if(bottom[col]<10) gameOn=false; return false; } public boolean test_bottom(int n){ if(can[0].y < bottom[col+n]) return true; return false; } public void draw(Graphics g){ if(next>0){ for(int i=0; i<next; i++){ cells[i].draw(g); } } can[0].draw(g); can[1].draw(g); can[2].draw(g); g.setColor(Color.black); g.drawRect(30,10,60,210); } }
CC4 is the applet class. The most important parts of this class to study are the lines dealing with events and the code within the run method.
import java.awt.*; import java.applet.*; import java.awt.event.*; import java.util.*; public class CC4 extends Applet implements Runnable{ Dimension d; Thread tm; Random r = new Random(); Image offI; int speed = 33; String title[] = { "C", "A", "N", "T", "R", "I", "S" }; GRID2 grid = new GRID2(); public void init(){ d = getSize(); offI=createImage(d.width,d.height); reset(); tm = new Thread(this); tm.start(); requestFocus(); this.addKeyListener(new KeyAdapter(){ public void keyPressed(KeyEvent k) { if(grid.keepPlaying()){ if(k.getKeyCode() == KeyEvent.VK_RIGHT){ grid.moveX(1); } else if(k.getKeyCode() == KeyEvent.VK_LEFT){ grid.moveX(-1); } else if(k.getKeyCode() == KeyEvent.VK_DOWN){ grid.rotate(); } else if(k.getKeyCode() == KeyEvent.VK_UP){ speed=100; } } else{ reset(); } repaint(); } }); } public void reset(){ grid=new GRID2(); } public void run(){ while(true){ while(grid.keepPlaying()){ if(grid.check_gaps()){ grid.settle_cells(); } else{ grid.move_can(); } if(grid.hit_bottom()){ grid.scoreGrid(); grid.spawn(); } repaint(); try{ Thread.sleep(speed); }catch(InterruptedException e){}; } } } public void update(Graphics g){ paint(g); } public void paint(Graphics g){ Graphics offG = offI.getGraphics(); offG.setColor(Color.white); offG.fillRect(0,0,d.width,d.height); grid.draw(offG); offG.setColor(Color.black); offG.setFont(new Font("sansserif", Font.BOLD, 24)); for(int i = 0; i<title.length; i++){ offG.drawString(title[i], 5, 30+30*i); } offG.setFont(new Font("sansserif", Font.PLAIN, 12)); if( grid.score==0) offG.drawString("Keep playing.",15,d.height-5); else if(grid.keepPlaying()) offG.drawString("SCORE: " + grid.score, 15,d.height-5); else{ offG.drawString("Press 'r'.", 15, d.height-2); offG.drawString("SCORE: " + grid.score,15,d.height-17); } g.drawImage(offI, 0, 0, this); } }


ASSIGNMENT:

1) Alter the functionality of the up key by making it into a toggle key between slow speed (100) and fast speed (25). Remember that the values altered by the up key actually reflect the amount of pause between redraws.

2) Add a shuffle method (called by a press on the s key) to GRID2 which switches the top and bottom cell of the current can. Test your game to see if skillful use of this alteration makes it easier to score more points.