import java.util.Random; import javax.swing.*; //JFrame,JComponent,Timer import java.awt.*; //Dimension,Image,Toolkit,Graphics,Container,Color,Graphics2D //RenderingHints,GradientPaint,Font,Rectangle,AlphaComposite import java.awt.event.*;//ActionEvent,ActionListener,KeyAdapter,KeyEvent import java.applet.*; //Applet,AudioClip import java.net.*; //URL,MalformedURLException public class Puyo_Puyo extends JFrame { GamePane gp; //GamePane is subclass of JComponent on which the puyos are moved int width,height; int rows,cols; int puyo_len; //holds the length of the each puyo(because it is a square piece) Dimension screenSize; //holds the dimension of the screen interms of resolution public Puyo_Puyo() { super("Puyo_Puyo"); cols=6; // Can be set to any value, game rows*cols depends on this value rows=cols*2; screenSize= Toolkit.getDefaultToolkit().getScreenSize(); width=screenSize.width; height=screenSize.height; //Width and Height holds the screen resolution. //Different computers may have set with different resolutions. //If the Window size is static then it is differ to see from one computer to another. //To place the window at the middle of the screen at any resolution and //to adjust the window size and puyo size some calculations are taken here. //Normally resolution(for pc) is windth*height format with 8:6 ratio. //For puyo game(rows=12,colums=6) window take 1:2(wd*ht) ratio in screen resolution. //So in 8 parts of width(of screen) 2 parts is assigned to window widht and //and in 6 parts of height(of screen) 4 parts is assigned to window height to place //window at the middle of the screen. //for puyo(cell size) width and height are same //puyo size for 12 rows 6 colums puyo game puyo_len=(width/8)*2/cols; //or (height/6)*4/12 gp=new GamePane(puyo_len,rows,cols); Container c=getContentPane(); c.add(gp); setResizable(false); //placing window(added score board size) at the middle of the screen //3 puyos width is added to the window for score board display setBounds ((width/8)*3-puyo_len*3/2,(height/6)*1-puyo_len,(width/8)*2+puyo_len*3+6,(height/6)*4+25+puyo_len); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String args[]) { System.out.println("Starting Puyo_Puyo..."); JFrame.setDefaultLookAndFeelDecorated(true); //to set the look and feel for frame as defined int Java new Puyo_Puyo(); } } class GamePane extends JComponent implements ActionListener { static int rows,cols; static int scr[][]; //scr or(screen) array holds the information about puyos to display Node tetris; //Formation of Tetris is checked using this object Timer timer,timer1,timer2,anim_timer; //different timers used for animation of puyos Image img[]=new Image[4]; //holds 4 puyo images Image fpipe,bpipe; //holds the front and back part of pipe image //2 images of pipe used to get the experience of puyos coming out from pipe String files[]={"intro.mid","enter.wav","sound735.wav","blip.wav","sound65.wav","sound136.wav","tada.wav","sound713.wav"}; AudioClip clips[]=new AudioClip[8];//Array of audio clips to load the sound files Toolkit tk; //used to load the images Random rand; //this object used to generate puyos randomly in color. int rot; //used for the rotation of the puyos int len; //length of the puyo ie. width and height boolean reached; //generated puyo reached the bottom or in movement int count; //count of puyos when formed tetris to delete boolean started; //Game is started or not boolean gameOver,paused; int a,b; //The two puyos generate to be next are stored in a and b int level,score,pieces,removed_puyos;//pieces is number of joint puyos(single piece) generated //number of removed puyos by forming tetris int minscore; int anim; //to build the pixel by pixel animation (movement of generated puyos) float alpha,alpha1; boolean levelflag; public GamePane(int l,int r,int c) { len=l; //length of puyos is set by the Puyo_Puyo class where it is calculated and sent here rows=r+1; //number of rows is taken 12+1 for the easyness of generating puyos from pipe. //extra one row is occupied by pipe at the top. Only 12 rows are used for puyos. cols=c; //6 columns init(); //initializing game data //puyo images are selected depending on the length of puyos //length of puyo is calculated depending on the resolution of screen //In this game i used two types of puyos for //(i)800*600 and below resolutions //(ii)1024*768 and above resolutions //So images are loaded ofter length of puyos is calculated loadImages(); loadSounds(); generatePuyos(); //to start the generating puyos clips[0].loop(); addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if(e.getKeyCode()==KeyEvent.VK_ENTER) { //if game is not started then start the game by starting timer when enter is pressed //ig game is over then initialize the variables and start generating puyos also if(!started) { clips[0].stop(); clips[1].play(); setDelays(); timer.start(); started=true; } if(gameOver) { init(); generatePuyos(); clips[0].loop(); started=false; } if(paused) { init(); generatePuyos(); clips[0].loop(); started=false; } repaint(); } else if(e.getKeyCode()==KeyEvent.VK_LEFT && !reached && !paused)//move puyos left if each puyo not reached to ground { clips[3].play(); moveLeft(); } else if(e.getKeyCode()==KeyEvent.VK_RIGHT && !reached && !paused) { clips[3].play(); moveRight(); } else if(e.getKeyCode()==KeyEvent.VK_UP && !paused ) { clips[3].play(); if(!reached) rotate(); if(!started && level<19) //Before strting the game level++; } else if(e.getKeyCode()==KeyEvent.VK_DOWN && !paused) { clips[3].play(); moveDown(); if(!started && level>0) //Before strting the game level--; } else if(e.getKeyCode()==KeyEvent.VK_P && started && !gameOver) { //if game is already paused then resume it, other wise pause the game if(paused) { clips[1].play(); paused=false; alpha1=0.0f; timer.start(); //game is resumed } else { clips[2].play(); timer.stop(); paused=true; //game is paused } } else if(e.getKeyCode()==KeyEvent.VK_ESCAPE) { clips[2].play(); //if game is already paused then exit the game, other wise pause the game if( started && !gameOver) { if(paused) System.exit(0); //exit the game else { timer.stop(); paused=true; //game is paused } } else System.exit(0); } } }); setFocusable(true); //to set the keyboard focus on this game pane } public void init()//all variables declared above are initialized here { scr=new int[rows][cols]; rot=1; reached=true; count=0; started=false; gameOver=false; paused=false; a=0; b=0; level=0; score=0; pieces=-1; removed_puyos=0; minscore=50; anim=0; alpha=0.0f; alpha1=0.0f; levelflag=true; tk= Toolkit.getDefaultToolkit(); rand=new Random(); timer=new Timer(1000,this); //generates action event for each 1075 milli seconds when timer is started timer.setInitialDelay(0); //generates first event ofter 0 ms when timer starts timer1=new Timer(1000,this); timer2=new Timer(500,this); anim_timer=new Timer(50,this); anim_timer.start(); //starting the timer } public void loadImages()//loading images into the image array and pipe objects { String s=""; if(len>=42) s="_"; for(int i=0;i<img.length;i++) img[i]=tk.getImage("images\\puyo_"+s+(i+1)+".png"); fpipe=tk.getImage("images\\pipe"+s+"1.png"); bpipe=tk.getImage("images\\pipe"+s+".png"); } public void loadSounds()//loading souds into the AudioClip array { try{ for(int i=0;i<clips.length;i++) //Loading all the sound clips clips[i]=Applet.newAudioClip(new URL("file:"+System.getProperty("user.dir")+"\\sounds\\"+files[i])); } catch (MalformedURLException e) { System.err.println(e.getMessage()); } } public void setDelays() { int delay=0,delay1=0; for(int i=0;i<=level;i++)//to set the delays depending on the level { delay+=20*(4-i/5); delay1+=4-i/5; } if(level==20) { delay+=25; delay1+=1; } timer.setDelay(1075-delay); anim_timer.setDelay(52-delay1); anim_timer.restart(); } public void generatePuyos()//To generate Puyos at the top of the game window { //Checking Top of the game window is occupied by any puyos //if occupied then game is over int p; if(cols%2==0) p=cols/2-1; else p=cols/2; if(scr[0][p]==0 && scr[1][p]==0) { clips[4].play(); scr[0][p]=a; //a and b are randomly generated Puyos scr[1][p]=b; } else { clips[2].play(); timer.stop(); gameOver=true; //game is over return; } int r; //Generating puyos randomly for the next fall which are viewed at the right side of window //Odd numbers 1,3,5,7(for 4 colors) are used for generating puyos which are in movement //Even numbers 2,4,6,6 are used for fallen puyos on bottom of the window while((r=rand.nextInt(8))%2==0); a=r; while((r=rand.nextInt(8))%2==0); b=r; pieces++; rot=1; } public void actionPerformed(ActionEvent e) { if(e.getSource()==timer) //If Event is generated by the timer object { clips[5].play(); movePuyos(); } else if(e.getSource()==timer1) //If Event is generated by the timer1 object { clips[6].play(); erase_puyos(); } else if(e.getSource()==timer2) { clips[7].play(); fillVacated(); timer2.stop(); } //timer1 and timer2 are used for make delay b/w erasing puyos and filling vacated places by puyos repaint(); } public void movePuyos()//Moving generated puyos Downword { int flag=0; for(int i=rows-1;i>=0;i--) for(int j=0;j<cols;j++) if(scr[i][j]%2==1) { if(i==rows-1) //for each moving puyo ie. on last row 12 { scr[i][j]+=1; //When generated puyo reached the ground ie. 12 row reached=true; //then make it as fallen puyo by making it as even number //reached the ground } else if(scr[i+1][j]==0) //if the next row is empty { scr[i+1][j]=scr[i][j]; //for each generated puyo in moment increase the row number scr[i][j]=0; flag=1; //to build the movement for that puyo } else { scr[i][j]+=1; //If the next row contain any puyo then stop the movement reached=true; //of puyo by making it even } anim=0; //for pixel by pixel animation of puyos here animation starts } if(flag==0) //if flag is not set mean that there is no puyo is in moment erase_puyos(); //so erase puyo which form tetris } public void erase_puyos() { int flag=0; for(int i=0;i<rows;i++) for(int j=0;j<cols;j++) if(scr[i][j]>0) //for all color puyos { count=1; tetris=new Node(i,j);//create a node for puyo chkForTetris(i,j); //check that node attached to the tetris puyos of same color if(count>=4) //if tetris forms { removeAllTetris();//remove the puyos which form tetris flag=1; /////////////////////////////////////// //I made change here to remove the bug in this revised game (by moving 4 lines of code to the below if block) //ie. erasing all the chain combos formed at a time will be removed at a time. /////////////////////////////////////// } } if(flag==1) { timer.stop(); //stop the generating puyos timer1.start(); //erase puyos if there is any other form tetris with delay timer2.start(); //fill vacated places of erased puyos with remain by the law of gravity with delay return; } timer1.stop(); //If there is no puyos form tetris then stop timer for erasing puyos minscore=50; //min score for each puyo is initiated generatePuyos(); //start generating puyos if(!timer.isRunning()) timer.start(); } public void chkForTetris(int x,int y) { if(y<cols-1 && scr[x][y]==scr[x][y+1] && !existsInTetris(x,y+1)) { //check for the same color puyo at the right side of count++; //current puyo which is not already added to the current tetris addToTetris(x,y+1); //If there is then add that to the current tetris chkForTetris(x,y+1); //Then check this node can connected to any same color puyo } if(x<rows-1 && scr[x][y]==scr[x+1][y] && !existsInTetris(x+1,y)) { count++; addToTetris(x+1,y); //check at the down side chkForTetris(x+1,y); } if(y>0 && scr[x][y]==scr[x][y-1] && !existsInTetris(x,y-1)) { count++; addToTetris(x,y-1); //check at the left side chkForTetris(x,y-1); } //I found a bug here and i rectified it by adding below if block //Before iam not checked up side //but if below type of chain combo formed by puyos // * * // *** //then up right puyo cannot be added to the tetris to remove if(x>0 && scr[x][y]==scr[x-1][y] && !existsInTetris(x-1,y)) { count++; addToTetris(x-1,y); //check at the up side chkForTetris(x-1,y); } } public void addToTetris(int x,int y)//adding another node to the present tetris { tetris.setNext(new Node(x,y)); tetris.getNext().setPrev(tetris);//It is totally the linked list concept used here tetris=tetris.getNext(); } public boolean existsInTetris(int x,int y)//comparing with the all the nodes in present tetris { //that it is already exists or not Node n=tetris; while(n!=null) { if(n.getX()==x && n.getY()==y) return true; n=n.getPrev(); } return false; } public void removeAllTetris() { Node n=tetris; while(n!=null) { scr[n.getX()][n.getY()]=0; //removing puyos which are in tetris n=n.getPrev(); } removed_puyos+=count; if(removed_puyos>=50) //Change the level of the game depending on { //number of removed puyos. if(levelflag) level+=1; else level-=1; //Ofter playing the final level(here 20) the level decreased by 1 if(level==20) //It is decreased up to 15 and then it increases 20 levelflag=false; //So The Game is endlessly continued if player can play all levels perfectly without any minstake at any level. if(level==15 && !levelflag) levelflag=true; setDelays(); removed_puyos=0; } score+=minscore*(count-3)*count;//Score is calculated by using this formula minscore=minscore*count; //minscore in formula depends on the number of puyos formed in last tetris //Scoring is totally depends on the length of the chain and //number of chains formed by puyos at a single time } public void fillVacated() //vacated places formed by removed puyos are filled with the other puyos by the law of gravity { for(int i=rows-2;i>=0;i--) for(int j=0;j<cols;j++) if(scr[i][j]>0) //for all puyos { int k; for(k=i+1;k<=rows-1;k++) if(scr[k][j]>0) //any puyo exist below the current puyo up to the ground { scr[k-1][j]=scr[i][j]; //then move on to it if(i!=k-1) scr[i][j]=0; break; } else if(k==rows-1) //if no puyo exist below the current puyo up to the ground { scr[rows-1][j]=scr[i][j]; //then move on to the ground if(i!=rows-1) scr[i][j]=0; } } } public void moveLeft() //to move the coming down puyos one step left { for(int i=0;i<rows;i++) for(int j=0;j<cols;j++) if(scr[i][j]>0 && scr[i][j]%2==1 && j>0 )//for all the coming down puyos which are not in first column(or left column) { if(j<cols-1 && scr[i][j+1]%2==1 && scr[i][j-1]==0) //if two puyo are in horizontal { //if there is no puyo in left scr[i][j-1]=scr[i][j]; //move two puyo to one step left scr[i][j]=scr[i][j+1]; scr[i][j+1]=0; } else if(scr[i][j-1]==0 && scr[i+1][j-1]==0 )//if two puyo are in vertical & there is no puyos in left { scr[i][j-1]=scr[i][j]; scr[i+1][j-1]=scr[i+1][j]; //move two puyo to one step left scr[i][j]=0; scr[i+1][j]=0; } return; } } public void moveRight() //to move the coming down puyos one step right { for(int i=0;i<rows;i++) for(int j=cols-1;j>=0;j--) if(scr[i][j]>0 && scr[i][j]%2==1 && j<cols-1)//for all the coming down puyos which are not in last column(or right column) { if(j>0 && scr[i][j-1]%2==1 && scr[i][j+1]==0)//if two puyo are in horizontal { //if there is no puyo in right scr[i][j+1]=scr[i][j]; scr[i][j]=scr[i][j-1]; //move two puyo to one step right scr[i][j-1]=0; } else if(scr[i][j+1]==0 && scr[i+1][j+1]==0 )//if two puyo are in vertical & there is no puyos in right { scr[i][j+1]=scr[i][j]; scr[i+1][j+1]=scr[i+1][j]; //move two puyo to one step right scr[i][j]=0; scr[i+1][j]=0; } return; } } public void rotate() //to rotate the coming down puyos in clock wise direction by 90 degrees { for(int i=0;i<rows;i++) for(int j=0;j<cols;j++) if(scr[i][j]>0 && scr[i][j]%2==1)//for all the coming down puyos { //alway consider the puyo left and top for horizontal and vertical positions as present //because checking is taking from top to bottom //Two puyos can make only four different positions rotating 90 degrees each time if(rot==1) //first postion=vertical { //moving down puyo to the left of first puyo makes 90 degrees rotaion to get second postion if(j>0 && scr[i][j-1]==0)//is left side is empty or not { scr[i][j-1]=scr[i+1][j]; scr[i+1][j]=0; //If it is empty location move there rot=2; //change to second position } else if(j<cols-1 && scr[i][j+1]==0)//is left side is not empty, chk for right position { scr[i][j+1]=scr[i][j];//move present puyo to the right scr[i][j]=scr[i+1][j];//move down puyo to the present location scr[i+1][j]=0; rot=2; //change to second position } }//second postion=horizontal else if(rot==2 && i>1)//moving present puyo to the up of right puyo makes 90 degrees rotaion to get third position { scr[i-1][j+1]=scr[i][j];//move present puyo to the up of the right puyo scr[i][j]=0; rot=3; //change to third position }//third postion=vertcal (invert to the first postion in two puyos positions) else if(rot==3) //moving present puyo to the up of right puyo makes 90 degrees rotaion to get fourth position { if(j<cols-1 && scr[i+1][j+1]==0)//is right side of the down puyo is empty or not { scr[i+1][j+1]=scr[i][j]; //If it is empty location move there scr[i][j]=0; rot=4; //change to fourth position } else if(j>0 && scr[i+1][j-1]==0)//is left side of the down puyo is empty or not { scr[i+1][j-1]=scr[i+1][j]; //move down puyo to the left scr[i+1][j]=scr[i][j]; //move present puyo to the down scr[i][j]=0; rot=4; //change to fourth position } }//fourth position=horizontal (invert to the second postion in two puyos positions) else if(rot==4 && i<rows-1)//moving right puyo to the down of the current puyo makes 90 degrees rotaion to get first position { if(scr[i+1][j]==0)//is down position is empty or not { scr[i+1][j]=scr[i][j+1];//move right puyo to the down of the present puyo scr[i][j+1]=0; rot=1; //change to first position } } return; } } public void moveDown()//Moving generated moving puyos by one step down { for(int i=rows-1;i>=0;i--) for(int j=0;j<cols;j++) if(scr[i][j]%2==1)//For all moving puyos { if(i==rows-1) //if puyo is in last row { scr[i][j]=scr[i][j]+1;//making it as grounded reached=true; } else if(scr[i+1][j]>0 && scr[i+1][j]%2==0)//if next row of puyo contains another puyo { scr[i][j]=scr[i][j]+1;//then stop the current puyo at the current postion reached=true; } else { scr[i+1][j]=scr[i][j];//Move present puyo one step down scr[i][j]=0; } } repaint(); } public void paint(Graphics g) { g.setColor(Color.white); g.fillRect(0,0,len*cols,len*rows);//background fill with white color g.setColor(Color.black); g.fillRect(len*cols,0,len*3,len*rows);//Score board is filled with black color Graphics2D g2=(Graphics2D)g; g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON ); //designing score board with gradient colors as borders g2.setPaint(new GradientPaint(cols*len,0,new Color(50,50,50),cols*len+len/2,0,new Color(200,200,200),false)); g2.fill(new Rectangle(cols*len,0,len/2,rows*len)); g2.setPaint(new GradientPaint((cols+2)*len+len/2,0,new Color(200,200,200),(cols+3)*len,0,new Color(50,50,50),false)); g2.fill(new Rectangle((cols+2)*len+len/2,0,len/2,rows*len)); g2.setPaint(Color.white); //white background for next coming puyos display g.fill3DRect((cols+3/2)*len,len,len,len*2,true); //The next coming puyos showned or drawn here g.drawImage(img[a/2],(cols+3/2)*len,len,len,len,null); g.drawImage(img[b/2],(cols+3/2)*len,len*2,len,len,null); //Score board designed with 3 rectangled as backgrounds for //Level and Number of pices and Total Score g.fill3DRect((cols)*len+len/2,(rows+1)*len/2-5,len*2,len-2,true); g.fill3DRect((cols)*len+len/2,(rows+1)*len/2+len-5,len*2,len-2,true); g.fill3DRect((cols)*len+len/2,(rows+1)*len/2+2*len-5,len*2,len,true); g2.setPaint(Color.black); //Font for the text to be display g2.setFont(new Font("Ariel",Font.PLAIN,len/3)); g2.drawString("Level: "+level,(cols)*len+len/2,rows*len/2+len); g2.drawString("pieces: "+pieces,(cols)*len+len/2,rows*len/2+2*len); g2.drawString("Score:"+score,(cols)*len+len/2,rows*len/2+3*len); //back part of pipe is drawn as image int p; if(cols%2==0) p=cols/2; else p=cols/2+1; g.drawImage(bpipe,p*len-len,0,len,len,null); for(int i=0;i<rows;i++) for(int j=0;j<cols;j++) if(scr[i][j]>0)//For all the puyos exists presently { int k=scr[i][j]; if(k%2==0)//if puyo value is even then simply display it { k=(int)k/2; g.drawImage(img[k-1],j*len,i*len,len,len,null); } else { //if puyo value is odd then add animation to it k=(int)k/2+1; g.drawImage(img[k-1],j*len,(i-1)*len+anim,len,len,null); anim+=2; if(anim>=len) anim=len; if(reached && i==2) reached=false; } } //front part of pipe is drawn as image g.drawImage(fpipe,p*len-len,0,len,len,null); //so when the puyo are at the postion of generation we get experience of puyo coming from the pipe if(!started)//if game is not started display the information { g2.setPaint(Color.yellow); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.8f)); g2.fill(new Rectangle(0,rows*len/4,(cols+3)*len,(rows+1)*len/2)); g2.setPaint(Color.blue); g2.setFont(new Font("Ariel",Font.PLAIN,len/2)); g2.drawString(" Level: "+level,len*3,rows*len/3); g2.setFont(new Font("Ariel",Font.PLAIN,len/3)); g2.drawString("Use the <up> and <down> arrow keys to change level now",len/4,(rows+2)*len/3); g2.setFont(new Font("Ariel",Font.PLAIN,len/2)); g2.drawString(" Press <Enter> to start the Game",len,rows*len/2); g2.setFont(new Font("Ariel",Font.PLAIN,len/3)); g2.drawString(" Use the left,right and down arrow keys to move the puyos.",0,(rows+1)*len/2); g2.drawString(" Pressing the up arrow key rotates the piece.",len,(rows+2)*len/2); g2.drawString(" When 4 or more puyos of the same colour are touching",len/3,(rows+3)*len/2); g2.drawString(" they disappear. ",len,(rows+4)*len/2); g2.drawString(" Press <p> to pause the Game",len,(rows+5)*len/2); g2.drawString(" Press <Escape> to exit the Game",len,(rows+6)*len/2); } if(gameOver)//if game is over dim the game by using alpha composite and display the information { g2.setPaint(Color.white); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,alpha)); if(alpha<0.9f) alpha=alpha+0.02f;//to build the animation of alpha blending g2.fill(new Rectangle(0,0,len*cols,len*rows)); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1.0f)); g2.setPaint(Color.red); g2.setFont(new Font("Ariel",Font.PLAIN,len/2)); g2.drawString("Game Over",len*3/2,rows*len/2); g2.setPaint(Color.blue); g2.setFont(new Font("Ariel",Font.PLAIN,len/3)); g2.drawString("Press <Enter> to restart the Game",len/2,(rows+1)*len/2); } if(paused)//if game is paused dim the game by using alpha composite and display the information { g2.setPaint(Color.white); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,alpha1)); if(alpha1<0.9f) alpha1=alpha1+0.02f; g2.fill(new Rectangle(0,0,len*cols,len*rows)); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1.0f)); g2.setPaint(Color.blue); g2.setFont(new Font("Ariel",Font.PLAIN,len/2)); g2.drawString("Game Paused",len*3/2,rows*len/2); g2.setFont(new Font("Ariel",Font.PLAIN,len/3)); g2.drawString(" Press <p> to resume the Game",len/2,(rows+1)*len/2); g2.drawString(" Press <Escape> to exit the Game",len/2,(rows+2)*len/2); g2.drawString(" Press <Enter> to restart the Game",len/2,(rows+3)*len/2); } } } class Node { //linked list is bulilt for the tetris of puyos to store their positions int x,y; Node nextnode; Node prevnode; public Node(int x,int y) { this.x=x; this.y=y; nextnode=null; prevnode=null; } public Node(Node p) { x=p.x; y=p.y; nextnode=null; prevnode=null; } public void setNext(Node lnode) { nextnode=lnode; } public Node getNext() { return nextnode; } public void setPrev(Node lnode) { prevnode=lnode; } public Node getPrev() { return prevnode; } public int getX() { return x; } public int getY() { return y; } }