Python实例浅谈之八2048游戏(字符界面)
来源:互联网 发布:玄幻网络作家排行榜 编辑:程序博客网 时间:2024/05/22 13:13
一、简介
使用python完成2048的代码编写。
2048游戏规则:简单的移动方向键让数字叠加,并且获得这些数字每次叠加后的得分,当出现2048这个数字时游戏胜利。同时每次移动方向键时,都会在这个4*4的方格矩阵的空白区域随机产生一个数字2或者4,如果方格被数字填满了,那么就GameOver了。
实现思路:2048游戏的全部操作都是围绕着一个4*4的矩阵进行,每次从用户界面获取用户的操作(即移动方向),然后重新计算这个4*4矩阵的状态,最后刷新用户界面显示4*4矩阵的最新状态,不断的循环这个过程,直到出现2048或没有空白方块了,如下是一个处理流程示意图:
二、运行图
Python控制台程序,用字符(W/S/A/D)代表方向键的输入,以数字0代表空白方格。计算部分:以向左移动为例,4*4矩阵在接收到向左移动的指令后,应该将每行的数字向左叠加, 将一行的叠加操作定义为函数 handle(list, direction),其第一个参数用来存储4*4矩阵中的某一行(列),第二个参数表示移动的方向(上下左右)。 这样当左右移动方向键时,可以这样来计算矩阵:遍历矩阵的每行,并将每行的数字沿左或右进行叠加操作,for row in matrix:handle(row, direction)。对于上下移动方向键时,由于矩阵是按行存储的,不能直接处理矩阵中的列,可以通过变通采用上面的函数handle()。对于矩阵中每一列,先将其拷贝到一个列表中,然后调用handle()函数对该列表进行叠加处理,最后再将叠加后的新列表拷贝回原始矩阵中其所在的列,其逻辑上等同于下面的代码操作。
handle(row, direction)函数的作用是沿指定方向叠加一行中的数字,请看下面几个例子:
实现handle函数是关键。仔细观察叠加的过程,其都是由两个子过程组成的:
三、详解
(1)文件2048.py,运行:python 2048.py,运行环境linux python2.6.6。# -*- coding:UTF-8 -*-#! /usr/bin/pythonimport randomimport sysv = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0] ]def display(v, score):'''显示界面'''print '{0:4} {1:4} {2:4} {3:4}'.format(v[0][0], v[0][1], v[0][2], v[0][3])print '{0:4} {1:4} {2:4} {3:4}'.format(v[1][0], v[1][1], v[1][2], v[1][3])print '{0:4} {1:4} {2:4} {3:4}'.format(v[2][0], v[2][1], v[2][2], v[2][3])print '{0:4} {1:4} {2:4} {3:4}'.format(v[3][0], v[3][1], v[3][2], v[3][3]), ' Total score: ', scoredef init(v):'''随机分布网格值'''for i in range(4):v[i] = [random.choice([0, 0, 0, 2, 2, 4]) for x in v[i]]def align(vList, direction):'''对齐非零的数字 direction == 'left':向左对齐,例如[8,0,0,2]左对齐后[8,2,0,0] direction == 'right':向右对齐,例如[8,0,0,2]右对齐后[0,0,8,2] '''# 移除列表中的0for i in range(vList.count(0)):vList.remove(0)# 被移除的0zeros = [0 for x in range(4 - len(vList))] # 在非0数字的一侧补充0if direction == 'left':vList.extend(zeros)else:vList[:0] = zeros def addSame(vList, direction): '''在列表查找相同且相邻的数字相加, 找到符合条件的返回True,否则返回False,同时还返回增加的分数 direction == 'left':从右向左查找,找到相同且相邻的两个数字,左侧数字翻倍,右侧数字置0 direction == 'right':从左向右查找,找到相同且相邻的两个数字,右侧数字翻倍,左侧数字置0'''score = 0if direction == 'left':for i in [0, 1, 2]:if vList[i] == vList[i+1] != 0: vList[i] *= 2vList[i+1] = 0score += vList[i]return {'bool':True, 'score':score}else:for i in [3, 2, 1]:if vList[i] == vList[i-1] != 0:vList[i-1] *= 2vList[i] = 0score += vList[i-1]return {'bool':True, 'score':score}return {'bool':False, 'score':score} def handle(vList, direction):'''处理一行(列)中的数据,得到最终的该行(列)的数字状态值, 返回得分 vList: 列表结构,存储了一行(列)中的数据 direction: 移动方向,向上和向左都使用方向'left',向右和向下都使用'right' '''totalScore = 0align(vList, direction)result = addSame(vList, direction)while result['bool'] == True:totalScore += result['score']align(vList, direction)result = addSame(vList, direction)return totalScore def operation(v):'''根据移动方向重新计算矩阵状态值,并记录得分 '''totalScore = 0gameOver = Falsedirection = 'left'op = raw_input('operator:')if op in ['a', 'A']: # 向左移动 direction = 'left' for row in range(4):totalScore += handle(v[row], direction)elif op in ['d', 'D']: # 向右移动 direction = 'right' for row in range(4): totalScore += handle(v[row], direction)elif op in ['w', 'W']: # 向上移动 direction = 'left' for col in range(4): # 将矩阵中一列复制到一个列表中然后处理 vList = [v[row][col] for row in range(4)] totalScore += handle(vList, direction) # 从处理后的列表中的数字覆盖原来矩阵中的值 for row in range(4): v[row][col] = vList[row]elif op in ['s', 'S']: # 向下移动 direction = 'right' for col in range(4): # 同上 vList = [v[row][col] for row in range(4)] totalScore += handle(vList, direction) for row in range(4): v[row][col] = vList[row]else: print('Invalid input, please enter a charactor in [W, S, A, D] or the lower') return {'gameOver':gameOver, 'score':totalScore} # 统计空白区域数目 NN = 0for q in v: N += q.count(0) # 不存在剩余的空白区域时,游戏结束if N == 0: gameOver = True return {'gameOver':gameOver, 'score':totalScore} # 按2和4出现的几率为3/1来产生随机数2和4num = random.choice([2, 2, 2, 4]) # 产生随机数k,上一步产生的2或4将被填到第k个空白区域k = random.randrange(1, N+1)n = 0for i in range(4): for j in range(4): if v[i][j] == 0: n += 1 if n == k: v[i][j] = num breakreturn {'gameOver':gameOver, 'score':totalScore}init(v)score = 0print 'Input:W(Up) S(Down) A(Left) D(Right), press <CR>.'while True: display(v, score) result = operation(v) if result['gameOver'] == True: print 'Game Over, You failed!' print 'Your total score:', score sys.exit(1) else: score += result['score'] if score >= 2048: print 'Game Over, You Win!!!' print 'Your total score:', score sys.exit(0)(2)修改出现例如(4,4,8,0)这样的情况,得分不合理的情况
# -*- coding:UTF-8 -*-#!/usr/bin/python2 import randomimport os, sys v = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] def display(v, score):print "%4d%4d%4d%4d"%(v[0][0], v[0][1], v[0][2], v[0][3])print "%4d%4d%4d%4d"%(v[1][0], v[1][1], v[1][2], v[1][3])print "%4d%4d%4d%4d"%(v[2][0], v[2][1], v[2][2], v[2][3])print "%4d%4d%4d%4d"%(v[3][0], v[3][1], v[3][2], v[3][3])print "Total score: %d" % score def init(v):for i in range(4):v[i] = [random.choice([0, 0, 0, 2, 2, 4]) for x in range(4)]def align(vList, direction):for i in range(vList.count(0)):vList.remove(0)zeros = [0 for x in range(4 - len(vList))]if direction == 'left':vList.extend(zeros)else:vList[:0] = zerosdef addSame(vList, direction):score = 0if direction == 'left':for i in [0, 1, 2]:align(vList, direction)if vList[i] == vList[i+1] != 0:vList[i] *= 2vList[i+1] = 0score += vList[i]return {'bool':True, 'score':score}else:for i in [3, 2, 1]:align(vList, direction)if vList[i] == vList[i-1] != 0:vList[i] *= 2vList[i-1] = 0score += vList[i]return {'bool':True, 'score':score}return {'bool':False, 'score':score}def handle(vList, direction):totalScore = 0align(vList, direction)result = addSame(vList, direction)while result['bool'] == True:totalScore += result['score']align(vList, direction)result = addSame(vList, direction)return totalScore def operation(v):totalScore = 0gameOver = Falsedirection = 'left'op = raw_input('operator:')if op in ['a','A']:direction = 'left'for row in range(4):totalScore += handle(v[row], direction)elif op in ['d','D']:direction = 'right'for row in range(4):totalScore += handle(v[row], direction)elif op in ['w', 'W']:direction = 'left'for col in range(4):vList = [v[row][col] for row in range(4)]totalScore += handle(vList, direction)for row in range(4):v[row][col] = vList[row]elif op in ['s', 'S']:direction = 'right'for col in range(4):vList = [v[row][col] for row in range(4)]totalScore += handle(vList, direction)for row in range(4):v[row][col] = vList[row]else:print "Invalid input,please enter a charactor in [W,S,A,D] or the lower"gameOver = Truereturn {'gameOver':gameOver,'score':totalScore}N = 0for q in v:N += q.count(0)if N == 0:gameOver = Truereturn {'gameover':gameOver,'score':totalScore}num = random.choice([2,2,2,4])k = random.randrange(1, N+1)n = 0for i in range(4):for j in range(4):if v[i][j] == 0:n += 1if n == k:v[i][j] = numbreakreturn {'gameOver':gameOver, 'score':totalScore}init(v)score = 0print "Input:W(Up) S(Down) A(Left) D(Right), press <CR>."while True:os.system("clear")display(v, score)result = operation(v)print resultif result['gameOver'] == True:print "Game Over, You failed!"print "Your total score %d" % (score)sys.exit(1)else:score += result['score']if score >= 2048:print "Game Over, You Win!!!"print "Your total score: %d" % (score)sys.exit(0)(3)linux使用ncurses库实现的2048 ai
/************************************makefile:*CC=gcc *CFLAGS=-g -O3 -lncurses **.PHONY : all *all: 2048.c * $(CC) 2048.c -o 2048 $(CFLAGS) *************************************---Play---- * make * ./2048 <-- Normal mode * ./2048 -s <-- Play with AI suggestion * ./2048 -a <-- Watch AI plays it. Good luck! ************************************/#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <ncurses.h>#include <sys/time.h>#include <time.h>#define AUTO#define SIZE (4)typedef int board_t[SIZE][SIZE];struct hash_elem { int hash; int depth; double value;};enum input { LEFT = 0, RIGHT = 1, UP = 2, DOWN = 3, QUIT = 4};/* Static mappings & initialzation ***********************************//* Weight of each value */int value_weight[16];/* Mapping from value to power of 2 form */int value_real[16];/* Default search depth for each #zero-blocks */int depth_map[] = {6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4};/* Weight map of cells */static const board_t cell_weight = { {17, 13, 11, 10}, {13, 10, 9, 9}, {11, 9, 8, 8}, {10, 9, 8, 8}};/* Used for hash table */static const board_t primes = { {22189, 28813, 37633, 43201}, {47629, 60493, 63949, 65713}, {69313, 73009, 76801, 84673}, {106033, 108301, 112909, 115249}}; void init() { int i; int cur_weight = 1; int cur_real = 2; for (i = 1; i < 16; i++) { value_weight[i] = cur_weight; value_real[i] = cur_real; cur_weight *= 3; cur_real *= 2; }}/* Util Functions *****************************************************/long gettime() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000 * 1000 + tv.tv_usec;}void draw_grid(int y, int x) { mvprintw(y++, x, "#####################################"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "#-----------------------------------#"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "#-----------------------------------#"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "#-----------------------------------#"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "# | | | #"); mvprintw(y++, x, "#####################################"); mvprintw(y+2, x, "Control: wasd Exit: q");}void board_dump(board_t b, int y, int x) { int i, j; draw_grid(y, x); for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { if (b[i][j]) mvprintw(i * 4 + 2 + y, j * 9 + 3 + x, "%d", value_real[b[i][j]]); } }}int board_count_zero(board_t b) { int cnt = 0; int i, j; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { if (b[i][j] == 0) cnt++; } } return cnt;}void board_clear(board_t b) { int i, j; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { b[i][j] = 0; } }}int board_hash(board_t b) { int i, j; int hash = 0; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { hash += b[i][j] * primes[i][j]; } } return hash;}void board_rnd_gen_cell(board_t b) { int i, j; int cnt = board_count_zero(b); int gen = random() % cnt; int n = (random() % 10) == 0 ? 2 : 1; cnt = 0; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { if (b[i][j] == 0) { if (cnt == gen) { b[i][j] = n; return; } cnt++; } } }}void delay() { struct timespec t; t.tv_sec = 0; t.tv_nsec = 10000000; nanosleep(&t, NULL);}/* Performance statistic */long stat_time[16];long stat_count[16]; void stat(int depth, long time) { stat_count[depth]++; stat_time[depth] += time;}void stat_dump() { int i; int line = 0; mvprintw(25 + line++, 8, "Performance Stat"); for (i = 0; i < 16; i++) { if (!stat_count[i]) continue; mvprintw(25 + line++, 8, "[Depth %d] %ld us * %d times", i, stat_time[i] / stat_count[i], stat_count[i]); } }/* Game logic: Move to a direction **************************************************//* Return score earned, return 1 if moved with zero score */#define movefunc(src_cell, combine_cell, nocombine_cell)\{\ int i, j = 0;\ int moved = 0;\ int score = 0;\ for (i = 0; i < SIZE; i++) {\ int last = 0;\ int j2 = 0;\ for (j = 0; j < SIZE; j++) {\ int v = src_cell;\ if (v == 0) {\ continue;\ }\ if (v == last) {\ last = 0;\ combine_cell = v + 1;\ score += value_real[v + 1];\ } else {\ if (j2 < j)\ moved = 1;\ last = v;\ nocombine_cell = v;\ j2++;\ }\ }\ }\ return score ? score : moved;\}#define REVERSE(i) (SIZE - 1 - (i))int move_left(board_t src, board_t dst) { movefunc(src[i][j], dst[i][j2 - 1], dst[i][j2]);}int move_right(board_t src, board_t dst) { movefunc(src[i][REVERSE(j)], dst[i][REVERSE(j2 - 1)], dst[i][REVERSE(j2)]);}int move_up(board_t src, board_t dst) { movefunc(src[j][i], dst[j2 - 1][i], dst[j2][i]);}int move_down(board_t src, board_t dst) { movefunc(src[REVERSE(j)][i], dst[REVERSE(j2 - 1)][i], dst[REVERSE(j2)][i]);}/* AI-related functions **************************************************/double value(board_t b, int depth, int *choice, double max);/* Immediate value score estimation for a board */int imm_value(board_t b) { int i, j; int result = 0; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { result += value_weight[b[i][j]] * cell_weight[i][j]; } } return result;}#define HASH_SIZE (35317) struct hash_elem vcache[HASH_SIZE];void cache_board_value(board_t b, int depth, double value) { int hash = board_hash(b); int index = hash % HASH_SIZE; vcache[index].hash = hash; vcache[index].value = value; vcache[index].depth = depth;}int qcnt;int qmiss;double query_board_value(board_t b, int depth) { int hash = board_hash(b); int index = hash % HASH_SIZE; qcnt++; if (vcache[index].hash == hash && vcache[index].depth >= depth) { return vcache[index].value; } qmiss++; return -1;}/* Generate 2/4 at every posible position, return the average value score * b : the board * depth : depth of the recursive search * max : current maximum value score * sampled : sample rate, 0 means no sample */double rnd_value(board_t b, int depth, double max, int sampled) { int i, j; int cnt = 0; double sum = 0; static int scnt = 0; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { /* Check zero */ if (b[i][j]) continue; /* Do sampling to reduce computation if needed */ if (sampled) { scnt++; if(scnt % sampled) continue; } cnt += 9; b[i][j] = 1; sum += 9 * value(b, depth, NULL, max); /* We do not take random 4 into consideration as it is rare */ if (depth >= 5) { cnt += 1; b[i][j] = 2; sum += value(b, depth, NULL, max); } /**/ b[i][j] = 0; } } return sum / cnt;}/* Return the value score for given board, zero score means died * b : the board * depth : depth of the recursive search * choice : used to return the final choise of movement * max : current maximum value score */double value(board_t b, int depth, int *choice, double max) { /* Estimate the value score */ int estimate = imm_value(b); /* Decrease depth if estimation is too low */ if (estimate < max * 0.7) depth--; /* Return estimation at level 0 */ if (depth <= 0) return estimate; /* Adjust next depth according to depth_map */ int next_depth = depth - 1; if (depth > 3) { int zeros = board_count_zero(b); if (next_depth > depth_map[zeros]) next_depth--; } int i; int moved[4]; double maxv = 0; board_t tmp[4] = {0}; int my_choice = QUIT; /* Default choice */ if (!choice) { double v = query_board_value(b, depth); if (v >= 0) return v; } moved[LEFT] = move_left(b, tmp[LEFT]); moved[RIGHT] = move_right(b, tmp[RIGHT]); moved[UP] = move_up(b, tmp[UP]); moved[DOWN] = move_down(b, tmp[DOWN]); /* Estimate the maximum value score */ if (depth > 2) for (i = 0; i < 4; i++) { int v = imm_value(tmp[0]); max = v > max ? v : max; } /* Try all the four direction */ for (i = 0; i < 4; i++) { int c; if (!moved[i]) continue; int sample = 0; //depth < 3 ? 3 : 1; double v = rnd_value(tmp[i], next_depth, max, sample); if (v > maxv) { my_choice = i; maxv = v; max = maxv; } } if (choice) *choice = my_choice; cache_board_value(b, depth, maxv); return maxv;}/* Game logic: Control and Interface *************************************/static int get_AI_input(board_t b) { int choice; int zeros = board_count_zero(b); long start = gettime(); double v = value(b, depth_map[zeros], &choice, 0); long timeval = gettime() - start; stat(depth_map[zeros], timeval); return choice;}static int get_keyboard_input() { char c; while(1) { //c = getchar(); c = getch(); switch(c) { case 'w': return UP; case 'a': return LEFT; case 's': return DOWN; case 'd': return RIGHT; case 'q': return QUIT; } }}int auto_play = 0;int suggestion = 0;void game_loop() { board_t a = {0}; board_t b = {0}; board_t *cur; board_t *next; int input; int AI_input; int score = 0; cur = &a; next = &b; board_rnd_gen_cell(*cur); while (1) { clear(); /* Draw the board */ board_dump(*cur, 4, 8); // stat_dump(); /* AI computation */ if (auto_play || suggestion) { AI_input = get_AI_input(*cur); const char *move_text[] = {"Left", "Right", "Up", "Down", "Game Over"}; mvprintw(1, 8, "Suggest: %s", move_text[AI_input]); } mvprintw(2, 8, "Score: %d", score); /* Update screen */ refresh(); /* Get input */ if (auto_play) { input = AI_input; } else { input = get_keyboard_input(); } int moved = 0; switch(input) { case UP: moved = move_up(*cur, *next); break; case LEFT: moved = move_left(*cur, *next); break; case DOWN: moved = move_down(*cur, *next); break; case RIGHT: moved = move_right(*cur, *next); break; case QUIT: return; default: continue; } if (!moved) continue; if (moved != 1) score += moved; /* Generate new cell */ board_rnd_gen_cell(*next); /* Switch cur and next */ board_t *temp = cur; cur = next; next = temp; board_clear(*next); }}int main(int argc, char *argv[]) { int opt; while ((opt = getopt(argc, argv, "as")) != -1) { switch (opt) { case 'a': auto_play = 1; break; case 's': suggestion = 1; break; default: /* '?' */ fprintf(stderr, "Usage: %s [-a] [-s]\r\n", argv[0]); fprintf(stderr, "-a: Let AI play the game\r\n"); fprintf(stderr, "-s: Display AI suggestion\r\n"); exit(EXIT_FAILURE); } } init(); srandom(time(NULL)); initscr(); noecho(); game_loop(); refresh(); endwin(); return 0;}编译:gcc -g -O3 -o 2048 2048.c -lncurses
运行:(第一图是正常模式,第二图是提示模式,第三张图是人工智能模式)
三、总结
(1)代码皆是总结而至,具体细节还待以后深入研究。
(2)代码中有比较多的问题,有待完善的地方还很多。
- Python实例浅谈之八2048游戏(字符界面)
- Android游戏开发之旅(八)SurfaceView类实例
- Python实例浅谈之七socket编程
- Python实例浅谈之六读写文件
- Python实例浅谈之二自定义异常
- Python实例浅谈之十国际化支持
- Python实例浅谈之国际化支持
- Python实例浅谈之二自定义异常
- python学习(八):字符编码
- Python:pygame游戏编程之旅四(游戏界面文字处理)
- Python:pygame游戏编程之旅五(游戏界面文字处理详解)
- Python:pygame游戏编程之旅四(游戏界面文字处理)
- Python:pygame游戏编程之旅五(游戏界面文字处理详解)
- unity3d学习笔记(八)--NGUI制作游戏界面
- unity3d学习笔记(八)--NGUI制作游戏界面
- C#八数码游戏实例
- Python实例浅谈之九使用本地socket文件
- Unity3D学习之(游戏注册界面)
- css搭建界面
- php图像处理常用函数
- Storm源码分析汇总
- static作用(修饰函数、局部变量、全局变量)
- android studio安装gradler安装不了的问题
- Python实例浅谈之八2048游戏(字符界面)
- JAVA设计模式初探之装饰者模式
- matlab添加SVM工具包
- AVA设计模式初探之组合模式
- 括号模拟题
- Mina框架学习笔记(五)
- extjs 动态调用已经创建的控件时兼容性问题,例如blur事件
- 【转载】J2SE知识点归纳笔记(二)---Java基础知识
- java集合类