Java制作智能拼圖游戲原理及代碼
今天突發(fā)奇想,想做一個智能拼圖游戲來給哄女友。
需要實現(xiàn)這些功能
第一圖片自定義
第二宮格自定義,當(dāng)然我一開始就想的是3*3 4*4 5*5,沒有使用3*5這樣的宮格。
第三要實現(xiàn)自動拼圖的功能,相信大家知道女人耍游戲都不是很厲害,所以這個自動拼圖功能得有。
其他什么暫停、排行就不寫了!
現(xiàn)在重點問題出來了
要實現(xiàn)自動拼圖功能似乎要求有點高哦!計算機有可不能像人一樣只能:
先追究下本質(zhì)
拼圖游戲其實就是排列問題:
排列有這么一個定義:在一個1,2,...,n的排列中,如果一對數(shù)的前后位置與大小順序相反,即前面的數(shù)大于后面的數(shù),那么它們就稱為一個逆序。一個排列中逆序的總數(shù)就稱為這個排列的逆序數(shù)。逆序數(shù)為偶數(shù)的排列稱為偶排列;逆序數(shù)為奇數(shù)的排列稱為奇排列。如2431中,21,43,41,31是逆序,逆序數(shù)是4,為偶排列。
再來一個定義:交換一個排列中的兩個數(shù),則排列的奇偶性發(fā)生改變。
以上定義都摘自《高等代數(shù)》。
拼圖排列必須是偶排列。這個在我參考文獻(xiàn)中可以找到。
所以我的只能拼圖是這樣實現(xiàn)的!
后續(xù)在寫
參考:http://en.wikipedia.org/wiki/Fifteen_puzzle
自動拼圖:
首先自動拼圖應(yīng)該有一定的規(guī)則,根據(jù)我拼圖的經(jīng)驗,要完成拼圖,不同區(qū)域使用的拼圖規(guī)則是不同的,所以:
我的宮格圖分為了4個區(qū)域(假如宮格圖是n*n個格子)
第一個區(qū)域:x坐標(biāo)范圍 0到n-2,y坐標(biāo)范圍 0到n-3
第二個區(qū)域:x坐標(biāo)n-1,y坐標(biāo)范圍 0到n-3
第三個區(qū)域:x坐標(biāo)范圍 0到n-3 ,y坐標(biāo)范圍 n-2和n-1
第四個區(qū)域:x坐標(biāo)范圍 n-2到n-1 ,y坐標(biāo)范圍 n-2和n-1;即最后四格
每個區(qū)域按照各自區(qū)域的規(guī)則即可完成
Puzzle.java
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.Random;
public class Puzzle {
private long step = 0;
private int n = 6;// 宮格基數(shù)
private int[][] puzzle;
private int resetBlock = 0;//
//空白塊位置
private int whiteBlockX;
private int whiteBlockY;
//當(dāng)前要準(zhǔn)備移動的塊的坐標(biāo)即復(fù)位塊
private int resetBlockX;
private int resetBlockY;
private boolean isPrint=false;
public Puzzle() {
init();
}
public Puzzle(int n) {
this.n = n;
init();
}
private void init() {
puzzle = new int[n][n];
for (int y = 0; y < n; y++) {
for (int x = 0; x < n; x++) {
puzzle[y][x] = x + y * n;
}
}
whiteBlockX = n-1;
whiteBlockY = n-1;
int times = 100;// 打亂次數(shù),必須是偶數(shù)
Random random = new Random();
while (times > 0) {
int x0 = random.nextInt(n);
int y0 = random.nextInt(n);
int x1 = random.nextInt(n);
int y1 = random.nextInt(n);
if (x0 != x1 && y0!=y1) {// 保證是偶排序
if((x0==n-1&&y0==n-1)||(x1==n-1&&y1==n-1)){//最后一個不調(diào)換
continue;
}
times--;
int t = puzzle[x0][y0];
puzzle[x0][y0] = puzzle[x1][y1];
puzzle[x1][y1] = t;
}
}
// int[][] p = {{22,9 ,1 ,5 ,0 ,25 },{
// 33,23,20,26,18,21},{
// 6 ,16,17,10,34,31},{
// 19,28,32,7 ,3 ,2},{
// 11,4 ,12,14,27,24},{
// 15,29,30,8 ,13,35}};
// puzzle = p;
}
public void sort(){
for (int y = 0; y < n; y++) {
for (int x = 0; x < n; x++) {
if (x == n - 1 && y == n - 1) {// 最后一個為空白,
} else {
reset(x, y);
}
}
}
}
//把塊復(fù)位移動目標(biāo)位置
private void reset(int targetX, int targetY) {
// 找到復(fù)位塊當(dāng)前的位置
initResetBlock(targetX, targetY);
/*
* 復(fù)位順序是從左到右,從上到下
* 移動方式 先上移動,再左移動
* 當(dāng)前復(fù)位塊,它要復(fù)位的位置可分為 四種情況
* 1、不在最右邊一行也不是最下面兩行
* 2、最右邊一行 x=n-1,但不是下面兩行;
* 3、最下面兩行 y=n-2,但不是最右邊一行;
* 4、即使最右邊的一行也是最下面兩行
*/
if(targetX < n-1 && targetY < n-2){
if(resetBlockX==targetX&&resetBlockY==targetY){//位置正確不用移動
return;//退出遞歸
}
resetBlockToTarget(targetX, targetY);
}else if(targetX==n-1 && targetY < n-2){//第二種情況
if(resetBlockX==targetX&&resetBlockY==targetY){//位置正確不用移動
return;//退出遞歸
}
reset2(targetX, targetY);
}else if(targetX < n-2 && targetY == n-2){
// isPrint=true;
reset3(targetX);
return;
}else{
initResetBlock(n-2, n-2);
resetBlockToTarget(n-2, n-2);
if(whiteBlockX<n-1){
whiteBlockRight();
}
if(whiteBlockY<n-1){
whiteBlockDown();
}
if(whiteBlockX==n-1&&whiteBlockY==n-1){
return;
}
}
reset(targetX, targetY);//遞歸
}
private void initResetBlock(int targetX,int targetY){
resetBlock = targetX + targetY * n;
for (int y = 0; y < n; y++) {
for (int x = 0; x < n; x++) {
if (puzzle[y][x] == resetBlock) {// x,y就是復(fù)位塊的位置
resetBlockX = x;
resetBlockY = y;
break;
}
}
}
}
private void reset3(int targetX){
// if(targetX>=2){
// }
initResetBlock(targetX, n-1);
resetBlockToTarget(targetX, n-2);
initResetBlock(targetX, n-2);
resetBlockToTarget(targetX+1, n-2);
l:
while (!(whiteBlockX==targetX && whiteBlockY==n-1)) {
if(whiteBlockY<n-1){
whiteBlockDown();
continue l;
}
if(whiteBlockX>targetX){
whiteBlockLeft();
continue l;
}
break;
}
whiteBlockUp();
swapWhiteBlockAndCurrentBlock();
if(puzzle[n-2][targetX]!=resetBlock||puzzle[n-1][targetX]!=(resetBlock+n)){//沒有復(fù)位成功
// isPrint=true;
swapWhiteBlockAndCurrentBlock();
reset3_0();
reset3(targetX);
}
}
private void reset3_0(){
if(resetBlockX<n-1){
whiteBlockDown();
whiteBlockRight();
whiteBlockRight();
whiteBlockUp();
swapWhiteBlockAndCurrentBlock();
reset3_0();
return;
}
return;
}
private void reset2_3(){
if(whiteBlockX==resetBlockX && whiteBlockY==resetBlockY+1){
return;//滿足條件,退出遞歸
}
//白塊可能在復(fù)位塊的:左方、左下、下方
if(whiteBlockY==resetBlockY){//左方
whiteBlockDown();
}else if(whiteBlockX < resetBlockX){//左下
whiteBlockRight();
}else {
whiteBlockUp();
}
reset2_3();//遞歸
}
private void reset2_2(int targetX, int targetY){
if(resetBlockX==targetX&&resetBlockY==targetY){//2、把復(fù)位塊移到目標(biāo)位置正下方
return;//退出遞歸
}
//復(fù)位塊可能位置,目標(biāo)位置左方、正下方、左下方
if(resetBlockX==targetX){//正下方 上移
resetBlockUp(targetX, targetY);
}else{//左方或左下方;先右移再上移
resetBlockRight(targetX, targetY);
}
reset2_2(targetX, targetY);//遞歸
}
private void reset2(int targetX, int targetY){
if(resetBlockX==targetX&&resetBlockY==targetY){//位置正確不用移動
return;//退出遞歸
}
/* 1、如果白塊正好占了目標(biāo)位置:如果復(fù)位塊正好在下方,交換及完成復(fù)位,如果下方不是復(fù)位塊,把白塊移開目標(biāo)位置
* 2、把復(fù)位塊移到目標(biāo)位置正下方
* 3、把白塊移動復(fù)位塊下方
* 4、按照規(guī)定的步驟復(fù)位
*/
//第一步
if(whiteBlockX==targetX&& whiteBlockY==targetY){
if(whiteBlockX==resetBlockX&&whiteBlockY==resetBlockY+1){//復(fù)位塊在下方
swapWhiteBlockAndCurrentBlock();
return;
}else{
whiteBlockDown();
}
}
//第二步 把復(fù)位塊移到目標(biāo)位置正下方
reset2_2(targetX, targetY+1);
//第三步 把白塊移動復(fù)位塊下方
reset2_3();
//第四步 按照規(guī)定的步驟復(fù)位
swapWhiteBlockAndCurrentBlock();
whiteBlockLeft();
whiteBlockUp();
whiteBlockRight();
whiteBlockDown();
whiteBlockLeft();
whiteBlockUp();
whiteBlockRight();
whiteBlockDown();
swapWhiteBlockAndCurrentBlock();
whiteBlockLeft();
whiteBlockUp();
whiteBlockUp();
whiteBlockRight();
swapWhiteBlockAndCurrentBlock();
}
private void resetBlockToTarget(int targetX, int targetY){
if(resetBlockX==targetX&&resetBlockY==targetY){//位置正確不用移動
return;//退出遞歸
}
if(resetBlockY==targetY){//正左
resetBlockLeft(targetX, targetY);
}else{//左下,下,右下
if(resetBlockX>=targetX){//右下||下;上移
if(resetBlockX==n-1){//復(fù)位塊在最右邊,先左移;方便上移時統(tǒng)一的采用白塊逆時針方式
resetBlockLeft(targetX, targetY);
}else{
resetBlockUp(targetX, targetY);
}
}else{//左下;右移
resetBlockRight(targetX, targetY);
}
}
resetBlockToTarget(targetX, targetY);//遞歸
}
private void resetBlockRight(int targetX, int targetY){
if(resetBlockX==targetX&&resetBlockY==targetY){//位置正確不用移動
return;//退出遞歸
}
if(resetBlockX==n-1){//復(fù)位塊在最右邊了,無法右移,直接退出
return;
}
// System.out.println("resetBlockRight");
if(whiteBlockY<resetBlockY){//上方
if(whiteBlockY<resetBlockY-1){//上方多行
whiteBlockDown();
}else{//上方一行
if(whiteBlockX<resetBlockX+1){//左上和正上
whiteBlockRight();
}else{//右上
whiteBlockDown();
}
}
}else if(whiteBlockY==resetBlockY){//同一行
if(whiteBlockX<resetBlockX){//左方
if(whiteBlockY==n-1){//到底了,只能往上
whiteBlockUp();
}else{
whiteBlockDown();
}
}else{//右方
if(whiteBlockX==resetBlockX+1){
swapWhiteBlockAndCurrentBlock();
return;//退出遞歸
}else{
whiteBlockLeft();
}
}
}else{//下方
if(whiteBlockX <= resetBlockX){//左下、下
whiteBlockRight();
}else{//右下
whiteBlockUp();
}
}
resetBlockRight(targetX, targetY);//遞歸
}
private void resetBlockLeft(int targetX, int targetY){
if(resetBlockX==targetX&&resetBlockY==targetY){//位置正確不用移動
return;//退出遞歸
}
if(resetBlockX==0){//在左邊邊界 復(fù)位塊無法左移,直接退出遞歸
return;
}
// System.out.println("resetBlockLeft");
if(whiteBlockY<resetBlockY){//上方
if(whiteBlockY<resetBlockY-1){//上方多行
whiteBlockDown();
}else{//上方一行
if(whiteBlockX==resetBlockX){//上方
if(whiteBlockX==n-1){//最右邊,白塊無法右移,只能左移
whiteBlockLeft();
}else{
if(resetBlockY==n-1){//復(fù)位塊在最低端,白塊不能順時針移動
whiteBlockLeft();
}else{
whiteBlockRight();
}
}
}else if(whiteBlockX>resetBlockX){//右上方
if(resetBlockY==n-1){//復(fù)位塊在最低端,白塊不能順時針移動
whiteBlockLeft();
}else{
whiteBlockDown();
}
}else{//左上方
whiteBlockDown();
}
}
}else if(whiteBlockY==resetBlockY){//左方、右方
if(whiteBlockX<resetBlockX){//左方
if(whiteBlockX==resetBlockX-1){//左邊一格
swapWhiteBlockAndCurrentBlock();//退出遞歸
return;
}else{
whiteBlockRight();
}
}else{//右方
if(whiteBlockY==n-1){//到底了,不能下移。只能上移
whiteBlockUp();
}else{
whiteBlockDown();
}
}
}else{//左下、下方、右下
if(whiteBlockX<resetBlockX){//左下
if(whiteBlockX==resetBlockX-1){
whiteBlockUp();
}else{
whiteBlockRight();
}
}else{//下方、右下
whiteBlockLeft();
}
}
resetBlockLeft(targetX, targetY);//遞歸
}
private void resetBlockUp(int targetX, int targetY){
if(resetBlockX==targetX&&resetBlockY==targetY){//位置正確不用移動
return;//退出遞歸
}
if(resetBlockY==0){//復(fù)位塊到頂了,無法上移
return;
}
// System.out.println("resetBlockUp");
if (whiteBlockY < resetBlockY) {//上方
if(whiteBlockY < resetBlockY - 1){//上方多行
whiteBlockDown();
}else{//上方一行
if(whiteBlockX == resetBlockX){//白塊和復(fù)位塊在同一列(豎列) 白塊和復(fù)位塊直接交換位置
swapWhiteBlockAndCurrentBlock();//退出遞歸
return;
}else{
if(whiteBlockX<resetBlockX){//白塊在復(fù)位塊的左邊;白塊右移
whiteBlockRight();
}else{//白塊在復(fù)位塊的右邊;白塊左移
whiteBlockLeft();
}
}
}
} else if (whiteBlockY == resetBlockY) {//白塊和復(fù)位塊同一行;白塊上移
if(whiteBlockX<resetBlockX){//正左
if(whiteBlockX<resetBlockX-1){//正左多格
whiteBlockRight();
}else{//正左一格
if(whiteBlockY==n-1){//到底了
whiteBlockUp();
}else {
if(resetBlockX==n-1){//復(fù)位塊在最右邊,無法逆時針,只有順指針移動白塊
whiteBlockUp();
}else{
whiteBlockDown();
}
}
}
}else{//正右
whiteBlockUp();
}
}else{//白塊在復(fù)位塊下方,白塊需要饒過復(fù)位塊上移,白塊逆時針繞到白塊上面
//三種情況:左下,下,右下
if(whiteBlockX<=resetBlockX){//左下,下;白塊右移
if(resetBlockX==n-1){//復(fù)位塊在最右邊,無法逆時針,只有順指針移動白塊
if(whiteBlockX==resetBlockX){//正下方
whiteBlockLeft();
}else{//左下方
whiteBlockUp();
}
}else{
whiteBlockRight();
}
}else{//右下;白塊上移
whiteBlockUp();
}
}
resetBlockUp(targetX, targetY);//遞歸
}
//白塊和復(fù)位塊交換位置
private void swapWhiteBlockAndCurrentBlock(){
step++;
int tempX = whiteBlockX,tempY = whiteBlockY;
int temp = puzzle[whiteBlockY][whiteBlockX];
puzzle[whiteBlockY][whiteBlockX] = puzzle[resetBlockY][resetBlockX];
puzzle[resetBlockY][resetBlockX] = temp;
whiteBlockX = resetBlockX;
whiteBlockY = resetBlockY;
resetBlockX = tempX;
resetBlockY = tempY;
println("swap");
}
private void whiteBlockDown(){
step++;
int temp = puzzle[whiteBlockY][whiteBlockX];
puzzle[whiteBlockY][whiteBlockX] = puzzle[whiteBlockY+1][whiteBlockX];
puzzle[whiteBlockY+1][whiteBlockX] = temp;
whiteBlockY++;
println("↓");
}
private void whiteBlockUp(){
step++;
int temp = puzzle[whiteBlockY][whiteBlockX];
puzzle[whiteBlockY][whiteBlockX] = puzzle[whiteBlockY-1][whiteBlockX];
puzzle[whiteBlockY-1][whiteBlockX] = temp;
whiteBlockY--;
println("↑");
}
private void whiteBlockLeft(){
step++;
int temp = puzzle[whiteBlockY][whiteBlockX];
puzzle[whiteBlockY][whiteBlockX] = puzzle[whiteBlockY][whiteBlockX-1];
puzzle[whiteBlockY][whiteBlockX-1] = temp;
whiteBlockX--;
println("←");
}
private void whiteBlockRight(){
step++;
int temp = puzzle[whiteBlockY][whiteBlockX];
puzzle[whiteBlockY][whiteBlockX] = puzzle[whiteBlockY][whiteBlockX+1];
puzzle[whiteBlockY][whiteBlockX+1] = temp;
whiteBlockX++;
println("→");
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("resetBlock=("+resetBlock+","+resetBlockX+","+resetBlockY+")\n");
if(puzzle!=null){
int len = String.valueOf(n*2-1).length();
for (int y = 0; y < n; y++) {
for (int x = 0; x < n; x++) {
if(x>0){
sb.append(",");
}
sb.append(_str(String.valueOf(puzzle[y][x]), len));
}
sb.append("\n");
}
sb.append("---------------------------------------");
}else{
sb.append("puzzle is null");
}
return sb.toString();
}
private String _str(String str,int len){
str=str==null?"":str;
if(str.length()<len){
return _str(str+" ", len);
}
return str;
}
private void println(String str){
if(isPrint){
System.out.println(str);
System.out.println(this);
}
}
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
// System.setOut(new PrintStream("e:/puzzle.txt","UTF-8"));
Puzzle p = new Puzzle();
System.out.println(p);
try {
p.sort();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exception:");
}finally{
System.out.println(p);
}
}
}
以上所述就是本文的全部內(nèi)容了,希望大家能夠喜歡。
相關(guān)文章
Intellij 下 mybatis 插件 MyBatisCodeHelperPro破解步驟詳解
這篇文章主要介紹了Intellij 下 mybatis 插件 MyBatisCodeHelperPro破解步驟,本文分步驟給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
登陸驗證碼kaptcha結(jié)合spring boot的用法詳解
在一個web應(yīng)用中驗證碼是一個常見的元素。不管是防止機器人還是爬蟲都有一定的作用,下面這篇文章主要給大家介紹了登陸驗證碼kaptcha結(jié)合spring boot用法的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-06-06
基于Java實現(xiàn)中文分詞系統(tǒng)的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實現(xiàn)一個簡易的中文分詞系統(tǒng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2022-07-07
Java實現(xiàn)BP神經(jīng)網(wǎng)絡(luò)MNIST手寫數(shù)字識別的示例詳解
這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)BP神經(jīng)網(wǎng)絡(luò)MNIST手寫數(shù)字識別的相關(guān)方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-01-01
Java中main函數(shù)的String[]?args用法舉例詳解
這篇文章主要給大家介紹了關(guān)于Java中main函數(shù)的String[]?args用法的相關(guān)資料,JAVA類中main函數(shù)的參數(shù)String[]?args指的是運行時給main函數(shù)傳遞的參數(shù),文中通過圖文以及代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12
Java實現(xiàn)的連續(xù)奇數(shù)(n+2*x)是合數(shù)的算法題暴力算法
java httpclient設(shè)置超時時間和代理的方法

