Δευτέρα 7 Νοεμβρίου 2022

Java Simple Snake Game


Jar with src (if jdk installed)

https://drive.google.com/file/d/1yNAFEdnr-p5pDZjh0kctfWyVPR0nkLNX/view?usp=sharing



 









(or compile and run ~ 700 lines)


/*

 * To change this license header, choose License Headers in Project Properties.

 * To change this template file, choose Tools | Templates

 * and open the template in the editor.

 */

package jsnake;


import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.Dimension;

import java.awt.Font;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.GridLayout;

import java.awt.Toolkit;

import java.awt.event.ComponentEvent;

import java.awt.event.ComponentListener;

import java.awt.event.ItemEvent;

import java.awt.event.KeyEvent;

import java.awt.event.KeyListener;

import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

import java.util.Random;

import java.util.Vector;

import javax.swing.JButton;

import javax.swing.JCheckBox;

import javax.swing.JComboBox;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JOptionPane;

import javax.swing.JPanel;

import javax.swing.JSpinner;

import javax.swing.SpinnerNumberModel;

import javax.swing.event.ChangeEvent;


/**

 *

 * @author jimak

 */

public class JSnake extends JPanel  implements MouseListener,KeyListener,ComponentListener{


    public static String getHelpString(){

        return ""

                + "Simple Java Snake Options"

                + "\nPress"

                + "\nArrows or W-up S-down A-left D-Right to turn snake's head."

                + "\n\nY/U to decrease/increase snake's speed."

                + "\nG/H to decrease/increase Rows and Cols."

                + ""

                + "\n\nT to change paint type"

                + "\nR to change snake's color."

                + "\nL to just ignore that you just lost.(just not show the red line)"

                + ""

                + "\n\nP to Restart by having almost half being filled."

                + "\n\nEnter to Restart ."

                + ""

                + ""

                + ""

                + ""

                + ""

                + ""

                + ""

                + ""

                + ""

                + ""

                + ""

                + "\n\n\n(you can still play if you loose and have some move to go)";

    }

    static Toolkit tk=Toolkit.getDefaultToolkit();

    static Dimension scr=tk.getScreenSize();

    static Random rand=new Random();

    public static Color randColor(){

        return new Color(rand.nextInt(255),rand.nextInt(255),rand.nextInt(255));

    }

    /**

     * @param args the command line arguments

     */

    public static void main(String[] args) {

        // TODO code application logic here

    

        try{

            JFrame jfr=new JFrame("Simple Java Snake Game");

            jfr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            int insw=scr.width/10;

            int insh=scr.height/10;

            jfr.setBounds(insw,insh,scr.width-2*insw,scr.height-2*insh);

            JSnake jsn=new JSnake();

            jfr.getContentPane().add(jsn);

            jfr.setVisible(true);

            JOptionPane.showMessageDialog(null,getHelpString());

            //throw new IllegalAccessError();

        }catch(Throwable thr){

            try{System.err.println(""+thr);}catch(Throwable thr2){}

            try{JOptionPane.showMessageDialog(null, thr,"will exit app",JOptionPane.ERROR_MESSAGE);}catch(Throwable thr2){}

            try{thr.printStackTrace();}catch(Throwable thr2){}

            System.exit(-1);

        }

        

    }

    

    static int dirs[][]=new int[4][2];

    public static final int dirRight=0;

    public static final int dirDown=1;

    public static final int dirLeft=2;

    public static final int dirUp=3;

    static{

        dirs[dirRight][0]=1;

        dirs[dirRight][1]=0;

        

        dirs[dirDown][0]=0;

        dirs[dirDown][1]=1;

        

        dirs[dirLeft][0]=-1;

        dirs[dirLeft][1]=0;

        

        

        dirs[dirUp][0]=0;

        dirs[dirUp][1]=-1;

    }

    public static int oppositeDir(int some){

        if(some==dirRight){return dirLeft;}

        if(some==dirLeft){return dirRight;}

        if(some==dirDown){return dirUp;}

        return dirDown;

    }

    

    JButton jbFillHalf=new JButton("Restart-Half");

    JButton jbRestart=new JButton("Restart");

    JSpinner jspRowsCols=new JSpinner(new SpinnerNumberModel(12,5,100,1));

    JPanel jpRowsCols=new JPanel(new BorderLayout());

    JSpinner jspMovesPerSecond=new JSpinner(new SpinnerNumberModel(3,1,10,1));

    JPanel jpMovesPerSecond=new JPanel(new BorderLayout());

    JPanel jpOthers=new JPanel(new GridLayout(1,2));

    

    JPanel jpRestartButs=new JPanel(new GridLayout(1,2));

    

    int rows,cols;

    int sqs;

    int headDir=0;

    byte snakex[];

    byte snakey[];

    int snakeLen=0;

    

    Graphics2D g;

    class SnakePainter{

        void paintSnakeSquare(int x,int y){

            g.fillRect(x+1, y+1, sqs-2,sqs-2);

        }

        public String toString(){return "fill Rect";}

    };

    SnakePainter currSnakePainter=new SnakePainter();

    SnakePainter snakePainter[]={

        currSnakePainter

        ,new SnakePainter(){

            void paintSnakeSquare(int x,int y){g.fillOval(x, y, sqs,sqs);}

            public String toString(){return "fill Oval";}

        }

        ,new SnakePainter(){

            void paintSnakeSquare(int x,int y){g.drawRect(x, y, sqs,sqs);}

            public String toString(){return "Quicker";}

        }

        ,new SnakePainter(){

            void paintSnakeSquare(int x,int y){g.drawOval(x, y, sqs,sqs);}

            public String toString(){return "draw Oval";}

        }

        ,new SnakePainter(){

            void paintSnakeSquare(int x,int y){g.drawOval(x, y, sqs,sqs);g.drawRect(x, y, sqs,sqs);}

            public String toString(){return "draws";}

        }

    };

    JComboBox<SnakePainter> jcombPaintType=new JComboBox<>(snakePainter);

    

    JPanel jpPaint=new JPanel(){

        public void paintComponent(Graphics gr){

            super.paintComponent(gr);

            g=(Graphics2D)gr;

            paintSnake();

        }

    };

    Thread thrRepainter=new Thread(){

        @Override

        public void run() { 

            while(true){

                jpPaint.repaint();

                try{sleep(20);}catch(InterruptedException inter){}

            

            }

        }

    };

    long thrMoverSleep=1000/(int)jspMovesPerSecond.getValue();

    boolean thrMoverPaused=false;

    Thread thrMover=new Thread(){

        @Override

        public void run() {

            while(true){

                if(thrMoverPaused){

                    try{sleep(600000);}catch(InterruptedException inter){}

                }

                else{

                    moveSnake();

                    try{sleep(thrMoverSleep);}catch(InterruptedException inter){}

                }

            }

        }

    };

    void thrMoverPause(){

        thrMoverPaused=true;

        thrMover.interrupt();

    }

    void thrMoverUnpause(){

        thrMoverPaused=false;

        thrMover.interrupt();

    }

    Font bigFont=new Font("Dialog",1,24);

    public JSnake(){

        super(new BorderLayout());

        jpPaint.setFont(bigFont);

        

        

        

        jpRestartButs.add(jbRestart);

        jpRestartButs.add(jbFillHalf);

        

        jpRowsCols.add(BorderLayout.WEST,new JLabel("Rows&Cols"));

        jpRowsCols.add(BorderLayout.CENTER,jspRowsCols);

        jpRowsCols.add(BorderLayout.EAST,jpRestartButs);

        

        jpMovesPerSecond.add(BorderLayout.WEST,new JLabel("moves/sec"));

        jpMovesPerSecond.add(BorderLayout.CENTER,jspMovesPerSecond);

        jpMovesPerSecond.add(BorderLayout.EAST,jcombPaintType);

        

        jpOthers.add(jpRowsCols);

        jpOthers.add(jpMovesPerSecond);

        

        add(BorderLayout.CENTER,jpPaint);

        add(BorderLayout.SOUTH,jpOthers);


        

        jspRowsCols(null);

        jbRestart();

        componentResized(null);

        

        

        

        jpPaint.addMouseListener(this);

        jpPaint.addKeyListener(this);

        

        addComponentListener(this);

        jspRowsCols.addChangeListener(e->jspRowsCols(e));

        jspMovesPerSecond.addChangeListener(e->jspMovesPerSecond());

        jbFillHalf.addActionListener(e->jbRestartHalf());

        jbRestart.addActionListener(e->jbRestart());

        jcombPaintType.addItemListener(e->jcombPaintType(e));

        

        

        Thread focuser=new Thread(){

            public void run(){

                try{sleep(1111);}catch(InterruptedException inter){}

                jpPaint.requestFocusInWindow();

                try{sleep(1111);}catch(InterruptedException inter){}

                jpPaint.requestFocusInWindow();

            }

        };

        focuser.setDaemon(true);

        thrMover.setDaemon(true);

        thrRepainter.setDaemon(true);

        

        

        

        thrRepainter.start();

        thrMover.start();

        focuser.start(); 

    }

    void jcombPaintType(ItemEvent e){

        if(e.getStateChange()==ItemEvent.SELECTED){

            currSnakePainter=(SnakePainter)jcombPaintType.getSelectedItem();

            jpPaint.requestFocusInWindow();

        }

    }

    void nextSnakePaintType(){

        int si=jcombPaintType.getSelectedIndex();

        ++si;

        if(si>=snakePainter.length){

            si=0;

        }

        jcombPaintType.setSelectedIndex(si);

    }

    void increaseSpeed(){

        int next=(int)jspMovesPerSecond.getValue();

        next+=1;

        if(next<=(int)((SpinnerNumberModel)jspMovesPerSecond.getModel()).getMaximum()){

            jspMovesPerSecond.setValue(next);

        }

        else{ tk.beep();}

    }

    void decreaseSpeed(){

        int next=(int)jspMovesPerSecond.getValue();

        next-=1;

        if(next>=(int)((SpinnerNumberModel)jspMovesPerSecond.getModel()).getMinimum()){

            jspMovesPerSecond.setValue(next);

        }

        else{tk.beep();}

    }

    void increaseRowsCols(){

        int next=(int)jspRowsCols.getValue();

        next+=1;

        if(next<=(int)((SpinnerNumberModel)jspRowsCols.getModel()).getMaximum()){

            jspRowsCols.setValue(next);

        }

        else{tk.beep();}

    }

    void decreaseRowsCols(){

        int next=(int)jspRowsCols.getValue();

        next-=1;

        if(next>=(int)((SpinnerNumberModel)jspRowsCols.getModel()).getMinimum()){

            jspRowsCols.setValue(next);

        }

        else{tk.beep();}

    }

    void jspMovesPerSecond(){

        long sec=1000;

        sec/=(int)jspMovesPerSecond.getValue();

        thrMoverSleep=sec;

    }

    void jspRowsCols(ChangeEvent e){

        int rc=(int)jspRowsCols.getValue();

        int rcs=rc*rc;

        byte neox[]=new byte[rcs];

        byte neoy[]=new byte[rcs];

        rows=rc;

        cols=rc;

        snakex=neox;

        snakey=neoy;

        jbRestart();

        componentResized(null);

        

    }

    void jbRestartHalf(){

        if(jbFillHalf.isEnabled()){

            thrMoverPause();

            int l=snakex.length;

            int half=l/2;

            int x=2;

            int y=3;

            if(rows<10){

                y=1;

            }

           

            headDir=dirRight;

            snakeLen=1;

            int i=0;

            snakex[i]=(byte)x;

            snakey[i]=(byte)y;

            ++y;

            ++i;

            boolean ystartOdd=y%2==1;

            while(y<rows){

                if(ystartOdd==(y%2==1)){

                    x=2;

                    while(x<cols){

                        snakex[i]=(byte)x;

                        snakey[i]=(byte)y;

                        ++i;++snakeLen;

                        ++x;

                    }

                }

                else{

                    x=cols-1;

                    while(x>1){

                        snakex[i]=(byte)x;

                        snakey[i]=(byte)y;

                        ++i;++snakeLen;

                        --x;

                    }

                    

                }

                ++y;

            }

            lost=false;won=false;

            createNewRandomApple();

            jpPaint.requestFocusInWindow();

            thrMoverUnpause();

            //jbFillHalf.setEnabled(false);

        }else{tk.beep();}

    }

    void jbRestart(){

        setAllEmpty(2,2,0);

    }

    void setAllEmpty(int snakeI,int snakeJ,int dir){

        thrMoverPause();

        jbFillHalf.setEnabled(true);

        

        lost=false;won=false;

        snakeLen=1;

        snakex[0]=(byte)snakeI;

        snakey[0]=(byte)snakeJ;

        headDir=dir;

        createNewRandomApple();

        jpPaint.requestFocusInWindow();

        thrMoverUnpause();

     }

    

    int applei=-1,applej=-1;

    int availables=0;

    void createNewRandomApple(){

        Vector<Integer> avXs=new Vector<>();

        Vector<Integer> avYs=new Vector<>();

        int i=0;

        while(i<rows){

            int j=0;

            while(j<cols){

                int s=0;

                boolean snakeHasThisSquare=false;

                while(s<snakeLen){

                    if(snakex[s]==j && snakey[s]==i){

                        snakeHasThisSquare=true;

                        s=snakeLen;

                    }

                    ++s;

                }

                if(!snakeHasThisSquare){

                    avXs.add(j);

                    avYs.add(i);

                }

                ++j;

            }

            ++i;

        }

        int size=avXs.size();

        availables=size-1;

        won=availables<rows;

        if(size>0){

            int appleIndex=rand.nextInt(size);

            applei=avXs.get(appleIndex);

            applej=avYs.get(appleIndex);

        }

        else{

            System.out.println("You won !!!! ");

            won=true;

        }

        //System.out.println("Availables: "+availables);

        

    }

    boolean snakeContains(int r,int c){

        int i=0;

        while(i<snakeLen){

            if(snakex[i]==r && snakey[i]==c){

                return true;

            }

            i++;

        }

        

        return false;

    }

    void moveSnake(){

        //System.out.println("moviung");

        int i=0;

        int headi=snakex[0];

        int headj=snakey[0];

        

        int nextheadi=headi+dirs[headDir][0];

        int nextheadj=headj+dirs[headDir][1];

        if(nextheadi<0 ||nextheadi>=cols || nextheadj<0 || nextheadj>=rows){

            lost=true;

        }

        else{

            if(snakeContains(nextheadi, nextheadj)){

                lost=true;

            }

            else{

                

                if(nextheadi==applei && nextheadj==applej){

                    //System.out.println("eating increase snake len");

                    int last=snakeLen;

                    while(last>0){

                        snakex[last]=snakex[last-1];

                        snakey[last]=snakey[last-1];

                        --last;

                    }

                    snakeLen++;

                    snakex[0]=(byte)nextheadi;

                    snakey[0]=(byte)nextheadj;

                    createNewRandomApple();

                    jpPaint.repaint();

                }

                else{

                    int last=snakeLen-1;

                    while(last>0){

                        snakex[last]=snakex[last-1];

                        snakey[last]=snakey[last-1];

                        --last;

                    }

                    snakex[0]=(byte)nextheadi;

                    snakey[0]=(byte)nextheadj;

                }

            }

        }

        

    }

    int headDir(){

        int out=-1;

        if(snakeLen>1){

            if(snakex[0]>snakex[1]){

                return dirRight;

            }

            else if(snakex[0]<snakex[1]){

                return dirLeft;

            }

            else if(snakey[0]>snakey[1]){

                return dirDown;

            }

            else{

                return dirUp;

            }

        }

        return out;

    }

    void setDir(int newDir){

        int dirCurr=headDir();

        if(dirCurr>-1){

            if(newDir!=oppositeDir(dirCurr)){

                headDir=newDir;

            }

            else{

                System.out.println("avoid set opposite direction");

            }

        }

        else{

            headDir=newDir;

        }

    }

    Color colSnake=Color.blue;

    Color colSq=Color.white;

    Color colWall=new Color(10,55,88,88);

    boolean lost=false;

    boolean won=false;

    int wall=10;

    int wall2=wall*2;

    void paintSnake(){

        g.setColor(colWall);

        int x=0;

        int y=0;

        g.fillRect(0, 0, wall2+(cols)*sqs,wall);//up wall

        g.fillRect(0, wall+((rows)*sqs),wall2+(cols)*sqs, wall);//down wall

        g.fillRect(x,wall2, wall, (rows)*sqs-wall2);//left wall

        g.fillRect(wall+cols*sqs,wall2, wall, (rows)*sqs-wall2);//right wall

        

        g.setColor(colSq);

        int i=0,j;

        x=wall;

        y=wall;

        while(i<rows){

            j=i%2;

            x=wall+j*sqs;

            while(j<cols){

                g.fillRect(x,y, sqs,sqs);

                j+=2;

                x+=sqs*2;

            }

            y+=sqs;

            ++i;

        }

        

        g.setColor(colSnake);

        i=0;

        

        while(i<snakeLen){

            x=snakex[i];

            y=snakey[i];

            x*=sqs;

            y*=sqs;

            x+=wall;

            y+=wall;

            currSnakePainter.paintSnakeSquare(x, y);

            //g.fillOval(x,y, sqs,sqs);

            //g.fillRect(x,y, sqs,sqs);

            //g.drawRect(x,y, sqs,sqs);

            //g.drawOval(x,y, sqs,sqs);

            ++i;

        }

        

        g.setColor(Color.red);

        g.fillRect(wall+applei*sqs, wall+applej*sqs, sqs,sqs);

        

        i=0;

        x=snakex[i];

        y=snakey[i];

        x*=sqs;

        y*=sqs;

        x+=sqs/2;

        y+=sqs/2;

        x+=wall;

        y+=wall;

        int xn=x+dirs[headDir][0]*sqs;

        int yn=y+dirs[headDir][1]*sqs;

        g.drawLine(x,y,xn,yn);

        

        

        if(lost){

            g.setColor(Color.RED);

            g.drawLine(0,0,1000,1000);

        }

        g.setColor(Color.green);

        g.drawString("Remains = "+availables,1,bigFont.getSize());

        if(won){

            g.setColor(Color.MAGENTA);

            g.translate((cols/2)*sqs, (rows/2)*sqs);

            g.rotate(theta);theta+=Math.toRadians(10);

            g.drawString("You won ",0,0);

        }

    }

    double theta=0;


    @Override

    public void mouseClicked(MouseEvent e) {

    }


    @Override

    public void mousePressed(MouseEvent e) {

        jpPaint.requestFocusInWindow();

    }


    @Override

    public void mouseReleased(MouseEvent e) {

    }


    @Override

    public void mouseEntered(MouseEvent e) {

    }


    @Override

    public void mouseExited(MouseEvent e) {

    }


    @Override

    public void keyTyped(KeyEvent e) {

    }


    @Override

    public void keyPressed(KeyEvent e) {

        int kc=e.getKeyCode();

        if((kc==KeyEvent.VK_UP||kc==KeyEvent.VK_W) && headDir!=dirDown){

            //System.out.println("up");

            setDir(dirUp);            

        }

        else if((kc==KeyEvent.VK_DOWN||kc==KeyEvent.VK_S) && headDir!=dirUp){

            //System.out.println("down");

            setDir(dirDown);

            

        }

        else if((kc==KeyEvent.VK_LEFT||kc==KeyEvent.VK_A) && headDir!=dirRight){

            //System.out.println("left");

            setDir(dirLeft);

            

        }

        else if((kc==KeyEvent.VK_RIGHT||kc==KeyEvent.VK_D) && headDir!=dirLeft){

            //System.out.println("right");

            setDir(dirRight);

            

        }

        else if(kc==KeyEvent.VK_ENTER){

            jbRestart();

        }

        else if(kc==KeyEvent.VK_P){

            jbRestartHalf();

        }

        else if(kc==KeyEvent.VK_Y){

            decreaseSpeed();

        }

        else if(kc==KeyEvent.VK_U){

            increaseSpeed();

        }

        else if(kc==KeyEvent.VK_G){

            decreaseRowsCols();

        }

        else if(kc==KeyEvent.VK_H){

            increaseRowsCols();

        }

        else if(kc==KeyEvent.VK_L){

            lost=false;

        }

        else if(kc==KeyEvent.VK_R){

            colSnake=randColor();

        }

        else if(kc==KeyEvent.VK_T){

            nextSnakePaintType();

        }

    }


    @Override

    public void keyReleased(KeyEvent e) {

    }


    @Override

    public void componentResized(ComponentEvent e) {

        int fitsrows=(jpPaint.getHeight()-wall*2)/(rows);

        int fitscols=(jpPaint.getWidth()-wall*2)/(cols);

        sqs=Math.min(fitscols, fitsrows);

        jpPaint.repaint();

    }


    @Override

    public void componentMoved(ComponentEvent e) {

    }


    @Override

    public void componentShown(ComponentEvent e) {

    }


    @Override

    public void componentHidden(ComponentEvent e) {

    }

    

    

    

    


}


Δεν υπάρχουν σχόλια: