八数码java_Java实战:八数码小游戏&八数码问题算法

相信学技术的你一定想把技术用于实际

(小编我就是从小玩游戏,然后就想自己做游戏了)

就酱紫一个小游戏,鼠标点击移动空白方格(别问为啥不用照片,用了图片都找不到北了)

还原成1 2 3 4 5 6 7 8就胜利了,(文章前面介绍如何做这样个游戏,文章后面将介绍解决八数码的算法)

我们可以先考虑游戏基本功能:新游戏,存档,继续游戏(读档),难度选择

(由于小编的游戏算法设计问题(后面提及),没设难度选项)

接下来就开正题了:

1.画出游戏基本窗体

LIKE THIS:

这里用个双重循环就可以解决,不过要搞清楚坐标,JFrame窗体的坐标从左上开始计算,那么就说明标题也占位置(大概20行像素点)。日常上测试代码:

public void paint(Graphics g)//重写paint,画的就不会消失{

super.paint(g);

initUI(g);

}

private Image bf;//用次画布,防止游戏闪屏private Graphics2D bfg;

private void initUI(Graphics g)

{

bf = this.createImage(getWidth(), getHeight());

bfg = (Graphics2D) bf.getGraphics();

bfg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);//开启抗锯齿Graphics g2 = bf.getGraphics();

g2.setColor(Color.white);

bfg.setColor(Color.DARK_GRAY);

//这里的size distance x0 y0都被我定义在一个接口里,这样可以方便理解//我想修改起始坐标(x0 y0)或间距distance或方格大小size只需要修改接口里数值bfg.fillRoundRect(x0, y0, 4 * distance + 3*size, 4 * distance + 3*size, 20, 20);

bfg.setColor(Color.white);

for(int i=0; i<3; i++)

{

for(int j=0; j<3; j++)

{

bfg.fillRect(x0+distance+(distance+size)*i, y0+distance+(distance+size)*j, size, size);

}

}

g.drawImage(bf, 0, 0, this);

}

2.产生数值并画到游戏方框中

生成随机排序有很多方法,小编用的随机交换数值的方法(即一开始定义它为123456780的规矩排序,然后随机交换之中的数值)(您也可以用您自己的方法,我的方法其实不好理解)

产生了之后就完事了???Nein!我们还得判断是否可以胜利

这个游戏就没法胜利是吧?所以还要用个算逆序数的算法来求是否可以胜利

public boolean AbleToWin(int[][] now) {//嘿嘿网上抄的,也不太懂其中奥妙int judge = 0;

for(int i=0; i<9; i++)

{

if(now[i/3][i%3]==0)continue;

for(int j=0; j

{

if(now[j/3][j%3]==0)continue;

if(now[i/3][i%3]

}

}

return judge%2==0;

}

然后咱设个循环,不能胜利就再求一次,直到可以胜利,再开始游戏

就把初始化界面完整代码附上了:

private void initUI(Graphics g)

{

bf = this.createImage(getWidth(), getHeight());

bfg = (Graphics2D) bf.getGraphics();

bfg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

Graphics g2 = bf.getGraphics();

g2.setColor(Color.white);

bfg.setColor(Color.DARK_GRAY);

bfg.fillRoundRect(x0, y0, 4 * distance + 3*size, 4 * distance + 3*size, 20, 20);

bfg.setColor(Color.white);

for(int i=0; i<3; i++)

{

for(int j=0; j<3; j++)

{

bfg.setColor(new Color(250-start[j][i]*10,240-start[j][i]*7,190-start[j][i]*8));//每个框颜色不同,才丰富嘛//您也可以用自己的方法设bfg.fillRect(x0+distance+(distance+size)*i, y0+distance+(distance+size)*j, size, size);

}

}

bfg.setColor(Color.black);

bfg.setFont(new Font("微软雅黑", Font.BOLD,size/2));//字体for(int i=0; i

for(int j=0; j

{//这里注意下,数组的x y坐标轴和窗体的正好相反 //反正多调调就出来了if(start[j][i]==0)

{//0就不画了,用空白代替,因为0是我们要移动的框g2.fillRect(x0+distance+(distance+size)*i, y0+distance+(distance+size)*j, size, size);

continue;

}

bfg.drawString(start[j][i]+"", x0+distance+(distance+size)*i+size/2-16, y0+distance+(distance+size)*j+size/2+17);

}

//testArray(start);多测试输出,是个好习惯g.drawImage(bf, 0, 0, this);

}

到时候效果是大概这样:

(玩游戏开了抗锯齿,小编玩游戏从来不敢开,电脑太次了,233)

3.给窗体加监听

(如何加监听就不说了哈)

不过我们要把鼠标点击位置映射到游戏方格,如下(下标从0开始)

得到相应的值之后还要判断值是否有效,比如上图中空白在(2,2)的位置,那么有效值就只有(1,2)和(2,1)(即空白的上下左右才有效)

得到有效值之后就可以移动(交换数值,并让窗体重绘一遍),然后每次移动都判断一次是否到达终点。这样之后,恭喜,您的小游戏就做完啦!您还可以加很多,比如难度选项

测试代码

public void mousePressed(MouseEvent e) {

x = e.getX();

y = e.getY();

x-=x0+distance;

y-=y0+distance;

x/=distance+size;

y/=distance+size;

//这里访问数组时x和y要反过来if(isNotValue(map, y, x));

else move(y, x);

}

private boolean isNotValue(int[][] now,int x, int y)

{//判断是否有效,要求在0的上下左右return x<0||x>=now[0].length||y<0||y>=now.length||!arround0(now, x, y);

}

private boolean arround0(int[][] now, int x, int y)

{//判断是否在空白周围int x0 = get0(now).getX();

int y0 = get0(now).getY();

for(int k=0; k<4; k++)

if((x0+moveX[k])==x && (y0+moveY[k])==y)return true;

return false;

}

void move(int x, int y)

{

steps++;//步数swap(map, get0(map).getX(),get0(map).getY(),x,y);//交换frame.repaint();

if(isWin(map)){

JOptionPane.showMessageDialog(frame, "You Win You've used "+steps+" steps!");

steps=0;

while(!frame.AbleToWin(map=getinitNumber()));

frame.setMap(map);

frame.repaint();

}

}

boolean isWin(int[][] now)

{

for(int i=0; i<9; i++)

if(now[i/3][i%3]!=win[i/3][i%3])return false;

return true;

}

void swap(int[][] target,int x1,int y1,int x2,int y2)

{

int t = target[x1][y1];

target[x1][y1] = target[x2][y2];

target[x2][y2] = t;

}

(怎么样,玩得开心不?有没有想过让他自动还原呢?那岂不是很酷?)

像这样:

这样多么吸引人啊,那怎么弄呢?

EXTRA:八数码问题

八数码问题的解有很多,因为是找最短路,就考虑广度优先搜索()

用康托展开来判重复(如果不是acm就用单广搜+康托展开就够了,速度还可以)

我把我的打包好了的模板:

import java.util.ArrayList;

public class AutoBot_V3 {

static final int fac[] = {1,1,2,6,24,120,720,5040,40320,362880};

static final int moveX[] = {-1, 1, 0, 0};

static final int moveY[] = { 0, 0,-1, 1};//上下左右static final char[] indexs = {'↑','↓','←', '→'};

private String path;

private int steps;

int[] startmap;

public AutoBot_V3(int[] tmpmap) {//把目前游戏的数字排成一维数组再传入startmap = tmpmap;

init();

}

class States{

int[] state;

int loc;

int step;

int hash;

String path;

public States(int[] state, int loc, int step, int hash, String path) {

this.state = state;

this.loc = loc;

this.step = step;

this.hash = hash;

this.path = path;

}

}

int cantor(int[] s)//康拖展开求该序列的hash值{

int sum=0;

for(int i=0;i<9;i++)

{

int num=0;

for(int j=i+1;j<9;j++)

if(s[j]

sum+=(num*fac[9-i-1]);

}

return sum+1;

}

void init()

{

boolean[] vis = new boolean[1000000];;

ArrayList q = new ArrayList();

int[] start = startmap;

int[] end = {1,2,3,4,5,6,7,8,0};

int aim = cantor(end);

vis[cantor(start)] = true;

int pos0=0;

for(int i=0; i<9;i++)

if(start[i]==0)pos0 = i;

States first = new States(start, pos0, 0, cantor(start), "");

q.add(first);

while(!q.isEmpty())

{

States cur = q.get(0);

q.remove(0);

int x = cur.loc/3;

int y = cur.loc%3;

for(int k=0; k<4; k++)

{

int tx = x + moveX[k];

int ty = y + moveY[k];

if(tx<0||tx>2||ty<0||ty>2)continue;

int[] tmp = new int[9];

for(int i=0; i<9; i++)tmp[i] = cur.state[i];

int nextpos = tx*3 + ty;

tmp[cur.loc] = tmp[nextpos];

tmp[nextpos] = 0;

int nexthash = cantor(tmp);

if(vis[nexthash])continue;

vis[nexthash] = true;

if(nexthash == aim)

{

path = cur.path+indexs[k];

steps = cur.step + 1;

return;

}

q.add(new States(tmp, nextpos, cur.step+1, nexthash, cur.path+indexs[k]));

}

}

}

public String getPath()

{

return path;

}

public int getStep()

{

return steps;

}

}

能看懂就看,看不懂会用就好

由于有了前面是否可以胜利的判断,这里不用担心搜不到了(吐槽下,java写算法比c++慢好多,而且没c++那么灵活)

这里可以得到路径和步数,然后怎么解决就取决于您啦!遍历一遍path就出来了

好了小游戏到此也做的差不多了,如果您有什么新的脑洞也可以加上去,有什么想法一定要在下面评论哟!

感谢您看完这么多东西,记得点个赞哟

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值