├── .gitignore ├── 2048python&c ├── 2048ai.c ├── ai.h ├── config.py ├── game.h ├── game.py ├── grid.h └── main.py ├── 2048python ├── ai.py ├── config.py ├── debug.py ├── game.py └── main.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /2048python&c/2048ai.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "grid.h" 6 | #include "game.h" 7 | #include "ai.h" 8 | 9 | int random_init(int a) { 10 | srand((unsigned)time(NULL)); 11 | } 12 | 13 | int ppow(int b) { 14 | int r = 1; 15 | for (; b; b--) { 16 | r *= 2; 17 | } 18 | return r > 1000 ? 1024 : r; 19 | } 20 | 21 | // int main(void) { 22 | // char *aa = {"LRD"}; 23 | // int a = 3, fuck = 27, i; 24 | // char *cc[a], **cp; 25 | // for(cp = cc + 3; cp != cc; cp--, *cp = aa); 26 | // i = 27; 27 | // do { 28 | // printf("%d, %c,%c,%c\n\n", i, **cc, **(cc + 1), **(cc + 2)); 29 | // cp = cc + a; 30 | // do { 31 | // if (**(--cp) == 'D') { 32 | // (*cp) -= 2; 33 | // } else { 34 | // (*cp)++; 35 | // break; 36 | // } 37 | // }while (cp != cc); 38 | // i--; 39 | // } while (i); 40 | // } 41 | 42 | int main() { 43 | game_init(4); 44 | game_start(); 45 | for (int i = 0; i < game.grid->length; i++) { 46 | game.grid->tiles[i] = i > 4 ? ppow(i) : 0; 47 | } 48 | ai_init(4); 49 | game.grid->print(game.grid); 50 | 51 | char a = get_next(); 52 | printf("运行: %c\n", a); 53 | game_run(a); 54 | game.grid->print(game.grid); 55 | 56 | a = get_next(); 57 | printf("%c\n", a); 58 | game_run(a); 59 | game.grid->print(game.grid); 60 | 61 | a = get_next(); 62 | printf("%c\n", a); 63 | game_run(a); 64 | game.grid->print(game.grid); 65 | } -------------------------------------------------------------------------------- /2048python&c/ai.h: -------------------------------------------------------------------------------- 1 | #ifndef AI_H 2 | #define AI_H 3 | // #include 4 | // #include 5 | // #include 6 | // #include 7 | // #include "grid.h" 8 | 9 | char* ACTIONS = "ULRD"; 10 | int SIZE; 11 | int LENGTH; 12 | Grid* tem_g = NULL; 13 | int _4_pows[] = {1, 4, 16, 64, 256, 1024, 4096, 16384}; 14 | 15 | typedef struct _Deci { 16 | char action_1; 17 | char action_2; 18 | char action_3; 19 | float score; 20 | } Deci; 21 | typedef struct _AI { 22 | Grid* g; 23 | } AI; 24 | 25 | AI ai; 26 | 27 | void ai_init(int size) { 28 | SIZE = size; 29 | LENGTH = size * size; 30 | ai.g = NewGrid(size); 31 | tem_g = NewGrid(SIZE); 32 | } 33 | 34 | int min(int a, int b) { 35 | return a > b ? b : a; 36 | } 37 | int max(int a, int b) { 38 | return a > b ? a : b; 39 | } 40 | int my_log2(int z) { 41 | return z; 42 | } 43 | 44 | int* get_grid(int* tiles, char** cc, int c_len) { 45 | memcpy(tem_g->tiles, tiles, sizeof(int) * LENGTH); 46 | for (; c_len; c_len--, cc++) { 47 | tem_g->run(tem_g, **cc, 0); 48 | tem_g->add_random_tile(tem_g); 49 | } 50 | return tem_g->tiles; 51 | } 52 | // 空格子数量 53 | int get_tile_num(int* tiles) { 54 | int n = 0, *p = tiles; 55 | for (int i = LENGTH; i; i--, p++) 56 | if (*p == 0) 57 | n++; 58 | return n; 59 | } 60 | 61 | int get_bj2__4(int* tiles) { 62 | int bj = 0, *z; 63 | for (int y = SIZE - 1; y; y--) 64 | for (int x = SIZE - 1; x; x--) { 65 | z = tiles + y * SIZE + x; 66 | if (*z < *(z - 1)) 67 | bj -= abs(my_log2(*(z - 1)) - *z); 68 | if (*z < *(z - SIZE)) 69 | bj -= abs(my_log2(*(z - SIZE)) - *z); 70 | if (*z < *(z - SIZE - 1)) 71 | bj -= abs(my_log2(*(z - SIZE - 1)) - *z); 72 | } 73 | return bj; 74 | } 75 | 76 | int get_bj__4(int* tiles) { 77 | int bj = 0, size = SIZE - 1, x, y, *z = tiles; 78 | for (y = 0; y < SIZE; y++) 79 | for (x = 0; x < SIZE; x++, z++) { 80 | if (*z) 81 | bj += (*z - 2) * (x + y - (size * 2 - 1)); 82 | else 83 | bj += 100 - 20 * (x + y - (size * 2 - 1)); 84 | } 85 | return bj; 86 | } 87 | 88 | float get_score(int* tiles) { 89 | // 格子数量(越少越好) 金角银边() 90 | int a = get_bj2__4(tiles); 91 | int b = get_bj__4(tiles); 92 | return a * 9.8 + b; 93 | } 94 | 95 | int combat(const void* a, const void* b) { 96 | return ((Deci*)a)->score - ((Deci*)b)->score; 97 | } 98 | 99 | char get_next() { 100 | int* tiles = game.grid->tiles; 101 | int tn = get_tile_num(tiles), i, j, fuck_step, fuck_step_2; 102 | float tem, min_fen; 103 | switch ((int)(((float)tn / ai.g->length) * 10)) { 104 | // case 5: 105 | // fuck_step = 2; 106 | // break; 107 | // case 4: 108 | // fuck_step = 3; 109 | // break; 110 | case 3: 111 | fuck_step = 3; 112 | break; 113 | case 2: 114 | fuck_step = 3; 115 | break; 116 | case 1: 117 | fuck_step = 3; 118 | break; 119 | case 0: 120 | fuck_step = 3; 121 | break; 122 | default: 123 | return rand() % 2 == 0 ? 'R' : 'D'; 124 | } 125 | // tn = fuck_step * 8; 126 | tn = min(max(tn * tn, 20), 40); 127 | // tn = 25; 128 | fuck_step_2 = _4_pows[fuck_step]; 129 | char *cc[fuck_step], **cp; 130 | Deci score_list[fuck_step_2]; 131 | Deci* ps = score_list; // ULRD 132 | for (cp = cc + fuck_step; cp != cc; cp--, *cp = ACTIONS); 133 | i = fuck_step_2; 134 | do { 135 | // printf("%d, %c,%c,%c,%c\n", i, **cc, **(cc + 1), **(cc + 2), **(cc + 3)); 136 | min_fen = get_score(get_grid(tiles, cc, fuck_step)); 137 | for (j = tn - 1; j; j--) { 138 | tem = get_score(get_grid(tiles, cc, fuck_step)); 139 | if (tem < min_fen) 140 | min_fen = tem; 141 | } 142 | ps->action_1 = **cc; 143 | ps->score = min_fen; 144 | ps++; 145 | 146 | cp = cc + fuck_step; 147 | do { 148 | if (**(--cp) == 'D') { 149 | (*cp) -= 3; 150 | } else { 151 | (*cp)++; 152 | break; 153 | } 154 | } while (cp != cc); 155 | i--; 156 | } while (i); 157 | 158 | qsort(score_list, fuck_step_2, sizeof(Deci), combat); 159 | ps = score_list + fuck_step_2 - 1; 160 | for (i = fuck_step_2; i; i--, ps--) { 161 | memcpy(ai.g->tiles, tiles, sizeof(int) * LENGTH); 162 | if (ai.g->run(ai.g, ps->action_1, 0) != 0) { 163 | // ps->score /= tn; 164 | return ps->action_1; 165 | } 166 | } 167 | ps = score_list + fuck_step_2 - 1; 168 | // ps->score /= tn; 169 | return ps->action_1; 170 | } 171 | 172 | #endif -------------------------------------------------------------------------------- /2048python&c/config.py: -------------------------------------------------------------------------------- 1 | 2 | class Base: 3 | WINDOW_W = 700 4 | WINDOW_H = 550 5 | GAME_WH = 500 6 | SIZE = 5 7 | FPS = 60 8 | DEBUG = False 9 | COLORS = { 10 | '0': (205, 193, 180), 11 | '2': (238, 228, 218), 12 | '4': (237, 224, 200), 13 | '8': (242, 177, 121), 14 | '16': (245, 149, 99), 15 | '32': (246, 124, 95), 16 | '64': (246, 94, 59), 17 | '128': (237, 207, 114), 18 | '256': (237, 204, 97), 19 | '512': (237, 200, 80), 20 | '1024': (237, 197, 63), 21 | '2048': (200, 63, 63), 22 | '4096': (170, 30, 70), 23 | '8192': (150, 30, 90), 24 | '16384': (120, 30, 110) 25 | } 26 | 27 | class SupperFast(Base): 28 | STEP_TIME = 0 29 | ANIMATION = False 30 | 31 | class Fast(Base): 32 | STEP_TIME = 0.3 33 | ANIMATION = True 34 | 35 | class Watch(Base): 36 | STEP_TIME = 0.9 37 | ANIMATION = True 38 | 39 | class Development(Base): 40 | STEP_TIME = 1.5 41 | ANIMATION = True 42 | DEBUG = True 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /2048python&c/game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | 4 | typedef struct _Game { 5 | int score; 6 | int state; 7 | int grid_size; 8 | Grid* grid; 9 | } Game; 10 | 11 | Game game; 12 | 13 | int game_get_score() { 14 | return game.score; 15 | } 16 | int game_get_state() { 17 | return game.state; 18 | } 19 | int game_get_titles(int x, int y) { 20 | return game.grid->tiles[y * game.grid_size + x]; 21 | } 22 | 23 | 24 | // 开始或重新开始 25 | void game_start(){ 26 | free(game.grid); 27 | game.grid = NewGrid(game.grid_size); 28 | game.grid->add_tile_init(game.grid); 29 | game.state = 1; 30 | } 31 | 32 | void game_init(int size) { 33 | game.score = 0; 34 | game.state = 0; 35 | game.grid_size = size; 36 | } 37 | // 运行一步 38 | void game_run(char direction){ 39 | if (game.state == 2) 40 | return; 41 | game.grid->run(game.grid, direction, 0); 42 | game.score += game.grid->score; 43 | if (game.grid->is_over(game.grid)) 44 | game.state = 2; 45 | // 产生新方块 46 | game.grid->add_random_tile(game.grid); 47 | } 48 | 49 | 50 | #endif -------------------------------------------------------------------------------- /2048python&c/game.py: -------------------------------------------------------------------------------- 1 | from ctypes import cdll 2 | 3 | nmap = {0: 'U', 1: 'R', 2: 'D', 3: 'L'} 4 | fmap = dict([val, key] for key, val in nmap.items()) 5 | 6 | C2048 = cdll.LoadLibrary("./2048.so") 7 | C2048.random_init(44444) 8 | 9 | 10 | class Ai: 11 | def __init__(self, grid_size=4): 12 | C2048.ai_init(grid_size) 13 | 14 | def get_next(self): 15 | return chr(C2048.get_next()) 16 | 17 | 18 | class Game: 19 | def __init__(self, grid_size=4): 20 | self.grid_size = grid_size 21 | C2048.game_init(grid_size) 22 | 23 | # 开始或重新开始 24 | def start(self): 25 | C2048.game_start() 26 | 27 | # 运行一步 28 | def run(self, direction): 29 | if isinstance(direction, int): 30 | direction = nmap[direction] 31 | C2048.game_run(ord(direction)) 32 | 33 | def get_state(self): 34 | return ["start", "run", "over"][C2048.game_get_state()] 35 | 36 | def get_score(self): 37 | return C2048.game_get_score() 38 | 39 | def get_titles(self, x, y): 40 | return C2048.game_get_titles(x, y) 41 | -------------------------------------------------------------------------------- /2048python&c/grid.h: -------------------------------------------------------------------------------- 1 | #ifndef GRID_H 2 | #define GRID_H 3 | 4 | // 二维向量 5 | typedef struct _Vector { 6 | int x; 7 | int y; 8 | } Vector; 9 | 10 | typedef struct _Grid { 11 | int* tiles; 12 | int size; 13 | int length; 14 | int score; 15 | int (*is_zero)(struct _Grid* self, int x, int y); 16 | int (*is_full)(struct _Grid* self); 17 | void (*set_tiles)(struct _Grid* self, Vector xy, int number); 18 | Vector (*get_random_xy)(struct _Grid* self); 19 | void (*add_random_tile)(struct _Grid* self); 20 | void (*add_tile_init)(struct _Grid* self); 21 | void (*move_hl)(struct _Grid* self, int ii, char direction); 22 | int (*run)(struct _Grid* self, char direction, int is_fake); 23 | int (*is_over)(struct _Grid* self); 24 | void (*print)(struct _Grid* self); 25 | } Grid; 26 | 27 | int is_zero(Grid* self, int x, int y) { 28 | return *(self->tiles + (self->size * y + x)) == 0 ? 1 : 0; 29 | } 30 | int is_full(Grid* self) { 31 | int* p = self->tiles; 32 | for (int i = self->length; i; i--) 33 | if (*p++ == 0) 34 | return 0; 35 | return 1; 36 | } 37 | // 设置瓷砖 38 | void set_tiles(Grid* self, Vector xy, int number) { 39 | self->tiles[self->size * xy.y + xy.x] = number; 40 | } 41 | // 获取一个随机的空坐标 42 | Vector get_random_xy(Grid* self) { 43 | Vector xy = {-1, -1}; 44 | if (self->is_full(self) == 0) { 45 | int size = self->size, x, y; 46 | while (1) { 47 | x = rand() % size, y = rand() % size; 48 | if (self->is_zero(self, x, y)) { 49 | xy.x = x; 50 | xy.y = y; 51 | break; 52 | } 53 | } 54 | } 55 | return xy; 56 | } 57 | // 添加一个随机的瓷砖 58 | void add_random_tile(Grid* self) { 59 | if (self->is_full(self) == 0) { 60 | int value = (float)rand() / RAND_MAX < 0.9 ? 2 : 4; 61 | self->set_tiles(self, self->get_random_xy(self), value); 62 | } 63 | } 64 | // 初始设置瓷砖 65 | void add_tile_init(Grid* self) { 66 | self->add_random_tile(self); 67 | self->add_random_tile(self); 68 | } 69 | // 移动某一行或某一列 70 | // ii: 要移动的行数还是列数 71 | // direction: 方向 72 | void move_hl(Grid* self, int ii, char direction) { 73 | int *hl[self->size], i, j, fuck = self->size * ii; 74 | int **p = hl, **q; 75 | switch (direction) { 76 | case 'U': 77 | for (i = 0; i < self->size; i++, p++) 78 | *p = self->tiles + (i * self->size + ii); 79 | break; 80 | case 'D': 81 | for (i = self->size - 1; i >= 0; i--, p++) 82 | *p = self->tiles + (i * self->size + ii); 83 | break; 84 | case 'L': 85 | for (i = 0; i < self->size; i++, p++) 86 | *p = self->tiles + (fuck + i); 87 | break; 88 | case 'R': 89 | for (i = self->size - 1; i >= 0; i--, p++) 90 | *p = self->tiles + (fuck + i); 91 | break; 92 | default: 93 | printf("move_hl: fuck=%X\n", direction); 94 | break; 95 | } 96 | p = hl; 97 | for (i = 0; i < self->size - 1; i++, p++) { 98 | if (**p == 0) 99 | for (j = self->size - i - 1, q = p + 1; j > 0; j--, q++) { 100 | if (**q != 0) { 101 | **p = **q; 102 | **q = 0; 103 | self->score++; 104 | break; 105 | } 106 | } 107 | if (**p == 0) 108 | break; 109 | for (j = self->size - i - 1, q = p + 1; j > 0; j--, q++) { 110 | if (**q == **p) { 111 | **p += **q; 112 | self->score += **q; 113 | **q = 0; 114 | break; 115 | } 116 | if (**q != 0) 117 | break; 118 | } 119 | } 120 | } 121 | // 运行 122 | int run(Grid* self, char direction, int is_fake) { 123 | if (direction != 'U' && direction != 'D' && direction != 'L' && direction != 'R') 124 | printf("run fuck: %x\n", direction); 125 | int *t, i; 126 | self->score = 0; 127 | if (is_fake) { 128 | t = (int*)malloc(sizeof(self->tiles)); 129 | memcpy(t, self->tiles, sizeof(self->tiles)); 130 | } else 131 | t = self->tiles; 132 | for (i = 0; i < self->size; i++) 133 | self->move_hl(self, i, direction); 134 | if (is_fake) free(t); 135 | return self->score; 136 | } 137 | // 判断是否结束 138 | int is_over(Grid* self) { 139 | if (self->is_full(self) == 0) 140 | return 0; 141 | int x, y, ys, ys1, tem, *t = self->tiles, s = self->size; 142 | for (y = s - 2; y >= 0; y--) { 143 | ys = y * s; 144 | ys1 = (y + 1) * s; 145 | for (x = s - 2; x >= 0; x--) { 146 | tem = *(t + (ys + x)); 147 | if (tem == *(t + (ys + x + 1)) || tem == *(t + (ys1 + x))) 148 | return 0; 149 | } 150 | } 151 | return 1; 152 | } 153 | void print(Grid* self){ 154 | printf("====================\n"); 155 | int x, y; 156 | for(y = 0; y < self->size; y ++) { 157 | for(x = 0; x< 5 * self->size + 1; x++) 158 | printf("-"); 159 | printf("\n"); 160 | for(x = 0; x< self->size; x++) { 161 | printf("|%4d", *(self->tiles + (self->size*y+x))); 162 | } 163 | printf("\n"); 164 | } 165 | for(x = 0; x< 5 * self->size + 1; x++) 166 | printf("-"); 167 | printf("\n"); 168 | printf("====================\n"); 169 | } 170 | Grid g_obj; 171 | int g_flag = 1; 172 | 173 | void CreateGrid() { // 提前创建对象 174 | // g_obj = (Grid*)malloc(sizeof(Grid)); 175 | // 赋值成员变量 176 | g_obj.score = 0; 177 | // 赋值成员方法 178 | g_obj.is_zero = is_zero; 179 | g_obj.is_full = is_full; 180 | g_obj.set_tiles = set_tiles; 181 | g_obj.get_random_xy = get_random_xy; 182 | g_obj.add_random_tile = add_random_tile; 183 | g_obj.add_tile_init = add_tile_init; 184 | g_obj.move_hl = move_hl; 185 | g_obj.run = run; 186 | g_obj.is_over = is_over; 187 | g_obj.print = print; 188 | g_flag = 0; 189 | } 190 | 191 | Grid* NewGrid(int size) { // new一个对象 192 | Grid* g = (Grid*)malloc(sizeof(Grid)); 193 | if (g_flag) 194 | CreateGrid(); 195 | memcpy(g, &g_obj, sizeof(Grid)); 196 | g->size = size; 197 | g->length = size * size; 198 | g->tiles = (int*)calloc(g->length, sizeof(int)); 199 | return g; 200 | } 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /2048python&c/main.py: -------------------------------------------------------------------------------- 1 | try: 2 | import pygame, os, time 3 | except: 4 | print('cmd run: pip3 install pygame -i https://mirrors.aliyun.com/pypi/simple') 5 | exit() 6 | from pygame.locals import * 7 | from game import Game, Ai 8 | from config import * 9 | 10 | config = SupperFast() 11 | 12 | FPS = config.FPS 13 | SIZE = config.SIZE 14 | DEBUG = config.DEBUG 15 | colors = config.COLORS 16 | GAME_WH = config.GAME_WH 17 | WINDOW_W = config.WINDOW_W 18 | WINDOW_H = config.WINDOW_H 19 | 20 | # 格子中的字体 21 | font_h_w = 2 / 1 22 | g_w = GAME_WH / SIZE * 0.9 23 | 24 | class Main(): 25 | def __init__(self): 26 | global FPS 27 | pygame.init() 28 | os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 50) 29 | self.set_win_wh(WINDOW_W, WINDOW_H, title='2048') 30 | self.state = 'start' 31 | self.fps = FPS 32 | self.catch_n = 0 33 | self.clock = pygame.time.Clock() 34 | self.game = Game(SIZE) 35 | self.ai = Ai(SIZE) 36 | self.step_time = config.STEP_TIME 37 | self.next_f = '' 38 | self.last_time = time.time() 39 | self.jm = -1 40 | 41 | def start(self): 42 | # 加载按钮 43 | self.button_list = [ 44 | Button('start', '重新开始', (GAME_WH + 50, 150)), 45 | Button('ai', '电脑托管', (GAME_WH + 50, 250)), 46 | ] 47 | self.run() 48 | 49 | def run(self): 50 | while self.state != 'exit': 51 | if self.game.get_state() in ['over', 'win']: 52 | self.state = self.game.get_state() 53 | self.my_event() 54 | if self.next_f != '' and ( 55 | self.state == 'run' or self.state == 'ai' and time.time() - self.last_time > self.step_time): 56 | self.game.run(self.next_f) 57 | self.next_f = '' 58 | self.last_time = time.time() 59 | elif self.state == 'start': 60 | self.game.start() 61 | self.state = 'run' 62 | self.set_bg((187, 173, 160)) 63 | self.draw_info() 64 | self.draw_button(self.button_list) 65 | self.draw_map() 66 | self.update() 67 | print('退出游戏') 68 | 69 | def draw_map(self): 70 | for y in range(SIZE): 71 | for x in range(SIZE): 72 | self.draw_block((x, y), self.game.get_titles(x, y)) 73 | if self.state == 'over': 74 | pygame.draw.rect(self.screen, (0, 0, 0, 0.5), 75 | (0, 0, GAME_WH, GAME_WH)) 76 | self.draw_text('游戏结束!', (GAME_WH / 2, GAME_WH / 2), size=25, center='center') 77 | elif self.state == 'win': 78 | pygame.draw.rect(self.screen, (0, 0, 0, 0.5), 79 | (0, 0, GAME_WH, GAME_WH)) 80 | self.draw_text('胜利!', (GAME_WH / 2, GAME_WH / 2), size=25, center='center') 81 | 82 | # 画一个方格 83 | def draw_block(self, xy, number): 84 | one_size = GAME_WH / SIZE 85 | dx = one_size * 0.05 86 | x, y = xy[0] * one_size, xy[1] * one_size 87 | color = colors.get(str(int(number)), (0, 0, 255)) 88 | pygame.draw.rect(self.screen, color, 89 | (x + dx, y + dx, one_size - 2 * dx, one_size - 2 * dx)) 90 | color = (20, 20, 20) if number <= 4 else (250, 250, 250) 91 | if number != 0: 92 | ln = len(str(number)) 93 | if ln == 1: 94 | size = one_size * 1.2 / 2 95 | elif ln <= 3: 96 | size = one_size * 1.2 / ln 97 | else: 98 | size = one_size * 1.5 / ln 99 | 100 | self.draw_text(str(int(number)), (x + one_size * 0.5, y + one_size * 0.5 - size / 2), color, size, 'center') 101 | 102 | def draw_info(self): 103 | self.draw_text('分数:{}'.format(self.game.get_score()), (GAME_WH + 50, 40)) 104 | if self.state == 'ai': 105 | self.draw_text('间隔:{}'.format(self.step_time), (GAME_WH + 50, 60)) 106 | self.draw_text('评分:{}'.format(self.jm), (GAME_WH + 50, 80)) 107 | 108 | def set_bg(self, color=(255, 255, 255)): 109 | self.screen.fill(color) 110 | 111 | def catch(self, filename=None): 112 | if filename is None: 113 | filename = "./catch/catch-{:04d}.png".format(self.catch_n) 114 | pygame.image.save(self.screen, filename) 115 | self.catch_n += 1 116 | 117 | def draw_button(self, buttons): 118 | for b in buttons: 119 | if b.is_show: 120 | pygame.draw.rect(self.screen, (180, 180, 200), 121 | (b.x, b.y, b.w, b.h)) 122 | self.draw_text(b.text, (b.x + b.w / 2, b.y + 9), size=18, center='center') 123 | 124 | def draw_text(self, text, xy, color=(0, 0, 0), size=18, center=None): 125 | font = pygame.font.SysFont('simhei', round(size)) 126 | text_obj = font.render(text, 1, color) 127 | text_rect = text_obj.get_rect() 128 | if center == 'center': 129 | text_rect.move_ip(xy[0] - text_rect.w // 2, xy[1]) 130 | else: 131 | text_rect.move_ip(xy[0], xy[1]) 132 | # print('画文字:',text,text_rect) 133 | self.screen.blit(text_obj, text_rect) 134 | 135 | # 设置窗口大小 136 | def set_win_wh(self, w, h, title='python游戏'): 137 | self.screen2 = pygame.display.set_mode((w, h), pygame.DOUBLEBUF, 32) 138 | self.screen = self.screen2.convert_alpha() 139 | pygame.display.set_caption(title) 140 | 141 | def update(self): 142 | self.screen2.blit(self.screen, (0, 0)) 143 | # 刷新画面 144 | # pygame.display.update() 145 | pygame.display.flip() 146 | time_passed = self.clock.tick(self.fps) 147 | 148 | # 侦听事件 149 | def my_event(self): 150 | if self.state == 'ai' and self.next_f == '': 151 | # self.next_f, self.jm = self.ai.get_next(self.game.grid.tiles) 152 | self.next_f = self.ai.get_next() 153 | for event in pygame.event.get(): 154 | if event.type == QUIT: 155 | self.state = 'exit' 156 | if event.type == KEYDOWN: 157 | if event.key == K_ESCAPE: 158 | self.state = 'exit' 159 | elif event.key in [K_LEFT, K_a] and self.state == 'run': 160 | self.next_f = 'L' 161 | elif event.key in [K_RIGHT, K_d] and self.state == 'run': 162 | self.next_f = 'R' 163 | elif event.key in [K_DOWN, K_s] and self.state == 'run': 164 | self.next_f = 'D' 165 | elif event.key in [K_UP, K_w] and self.state == 'run': 166 | self.next_f = 'U' 167 | elif event.key in [K_k, K_l] and self.state == 'ai': 168 | if event.key == K_k and self.step_time > 0: 169 | self.step_time *= 0.9 170 | if event.key == K_l and self.step_time < 10: 171 | if self.step_time != 0: 172 | self.step_time *= 1.1 173 | else: 174 | self.step_time = 0.01 175 | if self.step_time < 0: 176 | self.step_time = 0 177 | 178 | if event.type == MOUSEBUTTONDOWN: 179 | for i in self.button_list: 180 | if i.is_click(event.pos): 181 | self.state = i.name 182 | if i.name == 'ai': 183 | i.name = 'run' 184 | i.text = '取消托管' 185 | elif i.name == 'run': 186 | i.name = 'ai' 187 | i.text = '电脑托管' 188 | break 189 | 190 | 191 | def run(): 192 | Main().start() 193 | 194 | 195 | # 按钮类 196 | class Button(pygame.sprite.Sprite): 197 | def __init__(self, name, text, xy, size=(100, 50)): 198 | pygame.sprite.Sprite.__init__(self) 199 | self.name = name 200 | self.text = text 201 | self.x, self.y = xy[0], xy[1] 202 | self.w, self.h = size 203 | self.is_show = True 204 | 205 | def is_click(self, xy): 206 | return (self.is_show and 207 | self.x <= xy[0] <= self.x + self.w and 208 | self.y <= xy[1] <= self.y + self.h) 209 | 210 | 211 | if __name__ == '__main__': 212 | run() 213 | -------------------------------------------------------------------------------- /2048python/ai.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import numpy as np 3 | from game import Grid, Game 4 | from config import * 5 | 6 | config = Base() 7 | 8 | 9 | def get_grid(tiles, directions): 10 | g = Grid(config.SIZE) 11 | g.tiles = tiles.copy() 12 | for direction in directions: 13 | g.run(direction) 14 | g.add_random_tile() 15 | return g.tiles 16 | 17 | 18 | def printf(tiles): 19 | for row in tiles: 20 | for i in row: 21 | print("{:^6}".format(i), end='') 22 | print() 23 | 24 | 25 | def my_log2(z): 26 | if z == 0: 27 | return 0 28 | else: 29 | return z 30 | # return np.math.log2(z) 31 | 32 | 33 | class Ai: 34 | def __init__(self): 35 | self.g = Grid(config.SIZE) 36 | 37 | def get_next(self, tiles): 38 | score_list = [] 39 | tn = self.get_tile_num(tiles) 40 | if tn >= self.g.size ** 2 / 3: 41 | return "RD"[np.random.randint(0, 2)], 0 42 | kn = min(max(tn ** 2, 20), 40) 43 | for directions in itertools.product("ULRD", repeat=3): 44 | fen = [] 45 | for i in range(kn): 46 | t_g = get_grid(tiles, directions) 47 | fen.append(self.get_score(t_g)) 48 | print(directions, min(fen)) 49 | score_list.append([directions, min(fen)]) 50 | score_list = sorted(score_list, key=(lambda x: [x[1]])) 51 | # print(score_list) 52 | for d in score_list[::-1]: 53 | self.g.tiles = tiles.copy() 54 | if self.g.run(d[0][0], is_fake=False) != 0: 55 | return d[0][0], d[1] / kn 56 | self.g.tiles = tiles.copy() 57 | # print('===',score_list[-1][0][0]) 58 | return score_list[-1][0][0], score_list[-1][1] / kn 59 | 60 | def get_score(self, tiles): 61 | # 格子数量(越少越好) 金角银边() 62 | # bjs = [self.get_bj2(tiles)[i] * 2.8 + self.get_bj(tiles)[i] for i in range(4)] 63 | # return max(bjs) 64 | a = self.get_bj2__4(tiles) 65 | b = self.get_bj__4(tiles) 66 | print(a, b) 67 | return a * 2.8 + b 68 | 69 | def debug(self, tiles): 70 | print('\n=======开始判断========') 71 | print('移动前棋盘:') 72 | printf(tiles) 73 | score_list = [] 74 | for directions in itertools.product("ULRD", repeat=2): 75 | t_g = get_grid(tiles, directions) 76 | fen = self.get_score(t_g) 77 | score_list.append([directions, fen]) 78 | print('==={}=={}=='.format(directions, fen)) 79 | printf(t_g) 80 | score_list = sorted(score_list, key=(lambda x: [x[1]])) 81 | # print(score_list) 82 | for d in score_list[::-1]: 83 | # print('-->',d) 84 | self.g.tiles = tiles.copy() 85 | # print(self.g.run(d[0][0],is_fake=True)) 86 | if self.g.run(d[0][0], is_fake=True) != 0: 87 | # print('---异动前:') 88 | # print(self.g.tiles) 89 | # print('---异动后:') 90 | self.g.run(d[0][0]) 91 | # print(self.g.tiles) 92 | return d[0][0] 93 | # print('===',score_list[-1][0][0]) 94 | return score_list[-1][0][0] 95 | 96 | # 空格子数量 97 | def get_tile_num(self, tiles): 98 | # l = len(tiles) 99 | n = 0 100 | for row in tiles: 101 | for i in row: 102 | if i == 0: 103 | n += 1 104 | return n 105 | # return np.bincount(tiles)[0] 106 | 107 | def get_bj(self, tiles): 108 | gjs = [ 109 | self.get_bj__1(tiles), 110 | self.get_bj__2(tiles), 111 | self.get_bj__3(tiles), 112 | self.get_bj__4(tiles) 113 | ] 114 | return gjs 115 | 116 | def get_bj__4(self, tiles): 117 | bj = 0 118 | l = len(tiles) 119 | size = self.g.size - 1 120 | for y in range(l): 121 | for x in range(l): 122 | z = tiles[y][x] 123 | if z != 0: 124 | z_log = z - 2 125 | bj += z_log * (x + y - (size * 2 - 1)) 126 | else: 127 | bj += (100 - 20 * (x + y - (size * 2 - 1))) 128 | # print(z, "-- ", bj) 129 | return bj 130 | 131 | def get_bj__3(self, tiles): 132 | bj = 0 133 | l = len(tiles) 134 | size = self.g.size - 1 135 | for y in range(l): 136 | for x in range(l): 137 | z = tiles[y][x] 138 | if z != 0: 139 | z_log = z - 2 140 | bj += z_log * ((size - x) + y - (size * 2 - 1)) 141 | else: 142 | bj += (100 - 20 * ((size - x) + y - (size * 2 - 1))) 143 | return bj 144 | 145 | def get_bj__2(self, tiles): 146 | bj = 0 147 | l = len(tiles) 148 | size = self.g.size - 1 149 | for y in range(l): 150 | for x in range(l): 151 | z = tiles[y][x] 152 | if z != 0: 153 | z_log = z - 2 154 | bj += z_log * ((size - x) + (size - y) - (size * 2 - 1)) 155 | else: 156 | bj += (100 - 20 * ((size - x) + (size - y) - (size * 2 - 1))) 157 | return bj 158 | 159 | def get_bj__1(self, tiles): 160 | bj = 0 161 | l = len(tiles) 162 | size = self.g.size - 1 163 | for y in range(l): 164 | for x in range(l): 165 | z = tiles[y][x] 166 | if z != 0: 167 | z_log = z - 2 168 | bj += z_log * (x + (size - y) - (size * 2 - 1)) 169 | else: 170 | bj += (100 - 20 * (x + (size - y) - (size * 2 - 1))) 171 | return bj 172 | 173 | def get_bj2(self, tiles): 174 | gjs = [ 175 | self.get_bj2__1(tiles), 176 | self.get_bj2__2(tiles), 177 | self.get_bj2__3(tiles), 178 | self.get_bj2__4(tiles) 179 | ] 180 | return gjs 181 | 182 | def get_bj2__1(self, tiles): 183 | bj = 0 184 | l = len(tiles) 185 | for y in range(0, l - 1, 1): 186 | for x in range(l - 1, 0, -1): 187 | z = tiles[y][x] 188 | if tiles[y][x] < tiles[y][x - 1]: 189 | bj -= abs(my_log2(tiles[y][x - 1]) - z) 190 | if tiles[y][x] < tiles[y + 1][x]: 191 | bj -= abs(my_log2(tiles[y + 1][x]) - z) 192 | if tiles[y][x] < tiles[y + 1][x - 1]: 193 | bj -= abs(my_log2(tiles[y + 1][x - 1]) - z) 194 | return bj 195 | 196 | def get_bj2__2(self, tiles): 197 | bj = 0 198 | l = len(tiles) 199 | for y in range(0, l - 1): 200 | for x in range(0, l - 1): 201 | z = tiles[y][x] 202 | if tiles[y][x] < tiles[y][x + 1]: 203 | bj -= abs(my_log2(tiles[y][x + 1]) - z) 204 | if tiles[y][x] < tiles[y + 1][x]: 205 | bj -= abs(my_log2(tiles[y + 1][x]) - z) 206 | if tiles[y][x] < tiles[y + 1][x + 1]: 207 | bj -= abs(my_log2(tiles[y + 1][x + 1]) - z) 208 | return bj 209 | 210 | def get_bj2__3(self, tiles): 211 | bj = 0 212 | l = len(tiles) 213 | for y in range(l - 1, 0, -1): 214 | for x in range(0, l - 1): 215 | z = tiles[y][x] 216 | if tiles[y][x] < tiles[y][x + 1]: 217 | bj -= abs(my_log2(tiles[y][x + 1]) - z) 218 | if tiles[y][x] < tiles[y - 1][x]: 219 | bj -= abs(my_log2(tiles[y - 1][x]) - z) 220 | if tiles[y][x] < tiles[y - 1][x + 1]: 221 | bj -= abs(my_log2(tiles[y - 1][x + 1]) - z) 222 | return bj 223 | 224 | def get_bj2__4(self, tiles): 225 | bj = 0 226 | l = len(tiles) 227 | for y in range(l - 1, 0, -1): 228 | for x in range(l - 1, 0, -1): 229 | z = tiles[y][x] 230 | if z < tiles[y][x - 1]: 231 | bj -= abs(my_log2(tiles[y][x - 1]) - z) 232 | if z < tiles[y - 1][x]: 233 | bj -= abs(my_log2(tiles[y - 1][x]) - z) 234 | if z < tiles[y - 1][x - 1]: 235 | bj -= abs(my_log2(tiles[y - 1][x - 1]) - z) 236 | return bj 237 | 238 | 239 | if __name__ == '__main__': 240 | game = Game(4) 241 | game.grid.tiles = np.array([ 242 | [0, 0, 0, 0], 243 | [0, 32, 64, 128], 244 | [256, 512, 1024, 1024], 245 | [1024, 1024, 1024, 1024] 246 | ]) 247 | ai = Ai() 248 | print(game.grid) 249 | 250 | a = ai.get_next(game.grid.tiles) 251 | print(a) 252 | game.run(a[0]) 253 | print(game.grid) 254 | -------------------------------------------------------------------------------- /2048python/config.py: -------------------------------------------------------------------------------- 1 | 2 | class Base: 3 | WINDOW_W = 700 4 | WINDOW_H = 550 5 | GAME_WH = 500 6 | SIZE = 4 7 | FPS = 60 8 | DEBUG = False 9 | COLORS = { 10 | '0': (205, 193, 180), 11 | '2': (238, 228, 218), 12 | '4': (237, 224, 200), 13 | '8': (242, 177, 121), 14 | '16': (245, 149, 99), 15 | '32': (246, 124, 95), 16 | '64': (246, 94, 59), 17 | '128': (237, 207, 114), 18 | '256': (237, 204, 97), 19 | '512': (237, 200, 80), 20 | '1024': (237, 197, 63), 21 | '2048': (255, 0, 0) 22 | } 23 | 24 | class SupperFast(Base): 25 | STEP_TIME = 0 26 | ANIMATION = False 27 | 28 | class Fast(Base): 29 | STEP_TIME = 0.3 30 | ANIMATION = True 31 | 32 | class Watch(Base): 33 | STEP_TIME = 0.9 34 | ANIMATION = True 35 | 36 | class Development(Base): 37 | STEP_TIME = 1.5 38 | ANIMATION = True 39 | DEBUG = True 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /2048python/debug.py: -------------------------------------------------------------------------------- 1 | try: 2 | import pygame, os, time 3 | except: 4 | print('cmd run: pip3 install pygame -i https://mirrors.aliyun.com/pypi/simple') 5 | exit() 6 | from pygame.locals import * 7 | from game import Game 8 | from ai import Ai 9 | from config import * 10 | 11 | config = Development() 12 | 13 | FPS = config.FPS 14 | SIZE = config.SIZE 15 | DEBUG = config.DEBUG 16 | colors = config.COLORS 17 | GAME_WH = config.GAME_WH 18 | WINDOW_W = config.WINDOW_W 19 | WINDOW_H = config.WINDOW_H 20 | 21 | class Main(): 22 | def __init__(self): 23 | global FPS 24 | pygame.init() 25 | os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100,50) 26 | self.set_win_wh(WINDOW_W,WINDOW_H,title='2048') 27 | self.state = 'start' 28 | self.fps = FPS 29 | self.catch_n = 0 30 | self.clock = pygame.time.Clock() 31 | self.game = Game(SIZE) 32 | self.ai = Ai() 33 | self.step_time = config.STEP_TIME 34 | self.next_f = '' 35 | self.last_time = time.time() 36 | 37 | def start(self): 38 | # 加载按钮 39 | self.button_list = [ 40 | Button('start','重新开始',(GAME_WH+50,100)), 41 | Button('ai','电脑托管',(GAME_WH+50,200)), 42 | ] 43 | self.run() 44 | 45 | def run(self): 46 | while self.state != 'exit': 47 | if self.game.state in ['over','win']: 48 | self.state = self.game.state 49 | self.my_event() 50 | if self.next_f!='' and (self.state =='run' or self.state =='ai' and time.time()-self.last_time>self.step_time): 51 | self.game.run(self.next_f) 52 | self.next_f='' 53 | self.last_time=time.time() 54 | elif self.state == 'start': 55 | self.game.start() 56 | self.state = 'run' 57 | self.set_bg((100,100,120)) 58 | self.draw_button(self.button_list) 59 | self.draw_map() 60 | self.update() 61 | print('退出游戏') 62 | 63 | def draw_map(self): 64 | for y in range(SIZE): 65 | for x in range(SIZE): 66 | self.draw_block((x,y),self.game.grid.tiles[y][x]) 67 | if self.state == 'over': 68 | pygame.draw.rect(self.screen, (0,0,0,0.5), 69 | (0, 0, GAME_WH, GAME_WH)) 70 | self.draw_text('游戏结束!',(GAME_WH/2,GAME_WH/2),size=25,center='center') 71 | elif self.state == 'win': 72 | pygame.draw.rect(self.screen, (0,0,0,0.5), 73 | (0, 0, GAME_WH, GAME_WH)) 74 | self.draw_text('胜利!',(GAME_WH/2,GAME_WH/2),size=25,center='center') 75 | 76 | # 画一个方格 77 | def draw_block(self,xy,number): 78 | one_size = GAME_WH/SIZE 79 | dx = one_size*0.1 80 | x, y = xy[0] * one_size, xy[1] * one_size 81 | # print(colors[str(int(number))]) 82 | color = colors[str(int(number))] if number<=2048 else (0,0,255) 83 | pygame.draw.rect(self.screen, color, 84 | (x + dx, y + dx, one_size - 2 * dx, one_size - 2 * dx)) 85 | if number<=4: 86 | color = (20,20,20) 87 | else: 88 | color = (250,250,250) 89 | if number!=0: 90 | self.draw_text(str(int(number)),(x+one_size*0.5,y+one_size*0.2),color,40,'center') 91 | 92 | def set_bg(self,color=(255,255,255)): 93 | self.screen.fill(color) 94 | 95 | def catch(self,filename=None): 96 | if filename is None: 97 | filename = "./catch/catch-{:04d}.png".format(self.catch_n) 98 | pygame.image.save(self.screen, filename) 99 | self.catch_n += 1 100 | 101 | def draw_button(self,buttons): 102 | for b in buttons: 103 | if b.is_show: 104 | pygame.draw.rect(self.screen, (180,180,200), 105 | (b.x, b.y, b.w, b.h)) 106 | self.draw_text(b.text,(b.x+b.w/2,b.y+9),size=18,center='center') 107 | 108 | def draw_text(self,text,xy,color=(0,0,0),size=18,center=None): 109 | font = pygame.font.SysFont('STXingkai', size) 110 | text_obj = font.render(text, 1, color) 111 | text_rect = text_obj.get_rect() 112 | if center == 'center': 113 | text_rect.move_ip(xy[0]-text_rect.w//2, xy[1]) 114 | else: 115 | text_rect.move_ip(xy[0], xy[1]) 116 | # print('画文字:',text,text_rect) 117 | self.screen.blit(text_obj, text_rect) 118 | 119 | # 设置窗口大小 120 | def set_win_wh(self,w,h,title = 'python游戏'): 121 | self.screen2 = pygame.display.set_mode((w, h), pygame.DOUBLEBUF, 32) 122 | self.screen = self.screen2.convert_alpha() 123 | pygame.display.set_caption(title) 124 | 125 | def update(self): 126 | self.screen2.blit(self.screen,(0,0)) 127 | # 刷新画面 128 | #pygame.display.update() 129 | pygame.display.flip() 130 | time_passed = self.clock.tick(self.fps) 131 | 132 | # 侦听事件 133 | def my_event(self): 134 | if self.state=='ai' and self.next_f=='': 135 | self.next_f = self.ai.get_next(self.game.grid.tiles) 136 | for event in pygame.event.get(): 137 | if event.type == QUIT: 138 | self.state = 'exit' 139 | if event.type == KEYDOWN: 140 | if event.key == K_ESCAPE: 141 | self.state = 'exit' 142 | if event.key in [K_LEFT, K_a] and self.state=='run': 143 | self.next_f = 'L' 144 | if event.key in [K_RIGHT, K_d] and self.state=='run': 145 | self.next_f = 'R' 146 | if event.key in [K_DOWN, K_s] and self.state=='run': 147 | self.next_f = 'D' 148 | if event.key in [K_UP, K_w] and self.state=='run': 149 | self.next_f = 'U' 150 | 151 | if event.type == MOUSEBUTTONDOWN: 152 | for i in self.button_list: 153 | if i.is_click(event.pos): 154 | self.state = i.name 155 | if i.name == 'ai': 156 | i.name = 'run' 157 | i.text = '取消托管' 158 | elif i.name == 'run': 159 | i.name = 'ai' 160 | i.text = '电脑托管' 161 | break 162 | 163 | def ai_ai(self): 164 | pass 165 | # nt = self.ai.get_next(self.game.grid.tiles) 166 | # self.game.run(nt) 167 | 168 | def run(): 169 | Main().start() 170 | 171 | # 按钮类 172 | class Button(pygame.sprite.Sprite): 173 | def __init__(self,name,text,xy,size=(100,50)): 174 | pygame.sprite.Sprite.__init__(self) 175 | self.name = name 176 | self.text = text 177 | self.x, self.y = xy[0], xy[1] 178 | self.w, self.h = size 179 | self.is_show = True 180 | 181 | def is_click(self,xy): 182 | if (self.is_show and 183 | self.x <= xy[0] <= self.x + self.w and 184 | self.y <= xy[1] <= self.y + self.h): 185 | return True 186 | else: 187 | return False 188 | 189 | if __name__ == '__main__': 190 | run() 191 | -------------------------------------------------------------------------------- /2048python/game.py: -------------------------------------------------------------------------------- 1 | import random 2 | import numpy as np 3 | 4 | 5 | class Grid: 6 | size = 4 7 | tiles = [] 8 | max_tile = 0 9 | 10 | def __init__(self, size=4): 11 | self.size = size 12 | self.score = 0 13 | self.tiles = np.zeros((size, size)).astype(np.int32) 14 | 15 | def is_zero(self, x, y): 16 | return self.tiles[y][x] == 0 17 | 18 | def is_full(self): 19 | return 0 not in self.tiles 20 | 21 | # 设置瓷砖 22 | def set_tiles(self, xy, number): 23 | self.tiles[xy[1]][xy[0]] = number 24 | 25 | # 获取一个随机的空坐标 26 | def get_random_xy(self): 27 | if not self.is_full(): 28 | while 1: 29 | x, y = random.randint(0, self.size - 1), random.randint(0, self.size - 1) 30 | if self.is_zero(x, y): 31 | return x, y 32 | return -1, -1 33 | 34 | # 初始设置瓷砖 35 | def add_tile_init(self): 36 | self.add_random_tile() 37 | self.add_random_tile() 38 | 39 | # 添加一个随机的瓷砖 40 | def add_random_tile(self): 41 | if not self.is_full(): 42 | # 产生2的概率为0.9 43 | # q = 0.9 44 | # for i in range(1,50): 45 | # if random.random() < q or i==50-1: 46 | # value = 2**i 47 | # break 48 | value = 2 if random.random() < 0.9 else 4 49 | self.set_tiles(self.get_random_xy(), value) 50 | 51 | def run(self, direction, is_fake=False): 52 | if isinstance(direction, int): 53 | direction = nmap[direction] 54 | self.score = 0 55 | if is_fake: 56 | t = self.tiles.copy() 57 | else: 58 | t = self.tiles 59 | if direction == 'U': 60 | for i in range(self.size): 61 | self.move_hl(t[:, i]) 62 | elif direction == 'D': 63 | for i in range(self.size): 64 | self.move_hl(t[::-1, i]) 65 | elif direction == 'L': 66 | for i in range(self.size): 67 | self.move_hl(t[i, :]) 68 | elif direction == 'R': 69 | for i in range(self.size): 70 | self.move_hl(t[i, ::-1]) 71 | return self.score 72 | 73 | # 移动某一行或某一列 74 | def move_hl(self, hl): 75 | ''' 76 | 移动某一行或某一列 77 | 对于hl,从大往小移动 78 | :return: 移动后的列表 79 | ''' 80 | len_hl = len(hl) 81 | for i in range(len_hl - 1): 82 | if hl[i] == 0: 83 | for j in range(i + 1, len_hl): 84 | if hl[j] != 0: 85 | hl[i] = hl[j] 86 | hl[j] = 0 87 | self.score += 1 88 | break 89 | if hl[i] == 0: 90 | break 91 | for j in range(i + 1, len_hl): 92 | if hl[j] == hl[i]: 93 | hl[i] += hl[j] 94 | self.score += hl[j] 95 | hl[j] = 0 96 | break 97 | if hl[j] != 0: 98 | break 99 | return hl 100 | 101 | # 判断是否结束 102 | def is_over(self): 103 | if not self.is_full(): 104 | return False 105 | for y in range(self.size - 1): 106 | for x in range(self.size - 1): 107 | if self.tiles[y][x] == self.tiles[y][x + 1] or self.tiles[y][x] == self.tiles[y + 1][x]: 108 | return False 109 | return True 110 | 111 | # 判断是否胜利 112 | def is_win(self): 113 | if self.max_tile > 0: 114 | return self.max_tile in self.tiles 115 | else: 116 | return False 117 | 118 | def __str__(self): 119 | str_ = '====================\n' 120 | for row in self.tiles: 121 | str_ += '-' * (5 * self.size + 1) + '\n' 122 | for i in row: 123 | str_ += '|{:4d}'.format(int(i)) 124 | str_ += '|\n' 125 | str_ += '-' * (5 * self.size + 1) + '\n' 126 | str_ += '==================\n' 127 | return str_ 128 | 129 | 130 | nmap = {0: 'U', 1: 'R', 2: 'D', 3: 'L'} 131 | fmap = dict([val, key] for key, val in nmap.items()) 132 | 133 | 134 | class Game: 135 | score = 0 136 | env = 'testing' 137 | state = 'start' 138 | grid = None 139 | 140 | def __init__(self, grid_size=4, env='production'): 141 | self.env = env 142 | self.grid_size = grid_size 143 | self.start() 144 | 145 | # 开始或重新开始 146 | def start(self): 147 | self.grid = Grid(self.grid_size) 148 | if self.env == 'production': 149 | self.grid.add_tile_init() 150 | self.state = 'run' 151 | 152 | # 运行一步 153 | def run(self, direction): 154 | if self.state in ['over', 'win']: 155 | return None 156 | if isinstance(direction, int): 157 | direction = nmap[direction] 158 | 159 | self.grid.run(direction) 160 | self.score += self.grid.score 161 | 162 | if self.grid.is_over(): 163 | self.state = 'over' 164 | 165 | if self.grid.is_win(): 166 | self.state = 'win' 167 | 168 | # 产生新方块 169 | if self.env == 'production': 170 | self.grid.add_random_tile() 171 | return self.grid 172 | 173 | def printf(self): 174 | print(self.grid) 175 | 176 | 177 | if __name__ == '__main__': 178 | game = Game(env='testing') 179 | # game.grid.set_tiles((0,0),2) 180 | # print(game.grid) 181 | # print(game.run('D')) 182 | print(game.move_hl([0, 0, 0, 2])) 183 | -------------------------------------------------------------------------------- /2048python/main.py: -------------------------------------------------------------------------------- 1 | try: 2 | import pygame, os, time 3 | except: 4 | print('cmd run: pip3 install pygame -i https://mirrors.aliyun.com/pypi/simple') 5 | exit() 6 | from pygame.locals import * 7 | from game import Game 8 | from ai import Ai 9 | from config import * 10 | 11 | # config = Development() 12 | config = SupperFast() 13 | 14 | FPS = config.FPS 15 | SIZE = config.SIZE 16 | DEBUG = config.DEBUG 17 | colors = config.COLORS 18 | GAME_WH = config.GAME_WH 19 | WINDOW_W = config.WINDOW_W 20 | WINDOW_H = config.WINDOW_H 21 | 22 | # 格子中的字体 23 | font_h_w = 2 / 1 24 | g_w = GAME_WH / SIZE * 0.9 25 | 26 | 27 | # font = pygame.font.SysFont('microsoftyahei', 20) 28 | 29 | class Main(): 30 | def __init__(self): 31 | global FPS 32 | pygame.init() 33 | os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (100, 50) 34 | self.set_win_wh(WINDOW_W, WINDOW_H, title='2048') 35 | self.state = 'start' 36 | self.fps = FPS 37 | self.catch_n = 0 38 | self.clock = pygame.time.Clock() 39 | self.game = Game(SIZE) 40 | self.ai = Ai() 41 | self.step_time = config.STEP_TIME 42 | self.next_f = '' 43 | self.last_time = time.time() 44 | self.jm = -1 45 | 46 | def start(self): 47 | # 加载按钮 48 | self.button_list = [ 49 | Button('start', '重新开始', (GAME_WH + 50, 150)), 50 | Button('ai', '电脑托管', (GAME_WH + 50, 250)), 51 | ] 52 | self.run() 53 | 54 | def run(self): 55 | while self.state != 'exit': 56 | if self.game.state in ['over', 'win']: 57 | self.state = self.game.state 58 | self.my_event() 59 | if self.next_f != '' and ( 60 | self.state == 'run' or self.state == 'ai' and time.time() - self.last_time > self.step_time): 61 | self.game.run(self.next_f) 62 | self.next_f = '' 63 | self.last_time = time.time() 64 | elif self.state == 'start': 65 | self.game.start() 66 | self.state = 'run' 67 | self.set_bg((101, 194, 148)) 68 | self.draw_info() 69 | self.draw_button(self.button_list) 70 | self.draw_map() 71 | self.update() 72 | print('退出游戏') 73 | 74 | def draw_map(self): 75 | for y in range(SIZE): 76 | for x in range(SIZE): 77 | self.draw_block((x, y), self.game.grid.tiles[y][x]) 78 | if self.state == 'over': 79 | pygame.draw.rect(self.screen, (0, 0, 0, 0.5), 80 | (0, 0, GAME_WH, GAME_WH)) 81 | self.draw_text('游戏结束!', (GAME_WH / 2, GAME_WH / 2), size=25, center='center') 82 | elif self.state == 'win': 83 | pygame.draw.rect(self.screen, (0, 0, 0, 0.5), 84 | (0, 0, GAME_WH, GAME_WH)) 85 | self.draw_text('胜利!', (GAME_WH / 2, GAME_WH / 2), size=25, center='center') 86 | 87 | # 画一个方格 88 | def draw_block(self, xy, number): 89 | one_size = GAME_WH / SIZE 90 | dx = one_size * 0.05 91 | x, y = xy[0] * one_size, xy[1] * one_size 92 | # print(colors[str(int(number))]) 93 | color = colors[str(int(number))] if number <= 2048 else (0, 0, 255) 94 | pygame.draw.rect(self.screen, color, 95 | (x + dx, y + dx, one_size - 2 * dx, one_size - 2 * dx)) 96 | color = (20, 20, 20) if number <= 4 else (250, 250, 250) 97 | if number != 0: 98 | ln = len(str(number)) 99 | if ln == 1: 100 | size = one_size * 1.2 / 2 101 | elif ln <= 3: 102 | size = one_size * 1.2 / ln 103 | else: 104 | size = one_size * 1.5 / ln 105 | 106 | self.draw_text(str(int(number)), (x + one_size * 0.5, y + one_size * 0.5 - size / 2), color, size, 'center') 107 | 108 | def draw_info(self): 109 | self.draw_text('分数:{}'.format(self.game.score), (GAME_WH + 50, 40)) 110 | if self.state == 'ai': 111 | self.draw_text('间隔:{}'.format(self.step_time), (GAME_WH + 50, 60)) 112 | self.draw_text('评分:{}'.format(self.jm), (GAME_WH + 50, 80)) 113 | 114 | def set_bg(self, color=(255, 255, 255)): 115 | self.screen.fill(color) 116 | 117 | def catch(self, filename=None): 118 | if filename is None: 119 | filename = "./catch/catch-{:04d}.png".format(self.catch_n) 120 | pygame.image.save(self.screen, filename) 121 | self.catch_n += 1 122 | 123 | def draw_button(self, buttons): 124 | for b in buttons: 125 | if b.is_show: 126 | pygame.draw.rect(self.screen, (180, 180, 200), 127 | (b.x, b.y, b.w, b.h)) 128 | self.draw_text(b.text, (b.x + b.w / 2, b.y + 9), size=18, center='center') 129 | 130 | def draw_text(self, text, xy, color=(0, 0, 0), size=18, center=None): 131 | font = pygame.font.SysFont('simhei', round(size)) 132 | text_obj = font.render(text, 1, color) 133 | text_rect = text_obj.get_rect() 134 | if center == 'center': 135 | text_rect.move_ip(xy[0] - text_rect.w // 2, xy[1]) 136 | else: 137 | text_rect.move_ip(xy[0], xy[1]) 138 | # print('画文字:',text,text_rect) 139 | self.screen.blit(text_obj, text_rect) 140 | 141 | # 设置窗口大小 142 | def set_win_wh(self, w, h, title='python游戏'): 143 | self.screen2 = pygame.display.set_mode((w, h), pygame.DOUBLEBUF, 32) 144 | self.screen = self.screen2.convert_alpha() 145 | pygame.display.set_caption(title) 146 | 147 | def update(self): 148 | self.screen2.blit(self.screen, (0, 0)) 149 | # 刷新画面 150 | # pygame.display.update() 151 | pygame.display.flip() 152 | time_passed = self.clock.tick(self.fps) 153 | 154 | # 侦听事件 155 | def my_event(self): 156 | if self.state == 'ai' and self.next_f == '': 157 | self.next_f, self.jm = self.ai.get_next(self.game.grid.tiles) 158 | for event in pygame.event.get(): 159 | if event.type == QUIT: 160 | self.state = 'exit' 161 | if event.type == KEYDOWN: 162 | if event.key == K_ESCAPE: 163 | self.state = 'exit' 164 | elif event.key in [K_LEFT, K_a] and self.state == 'run': 165 | self.next_f = 'L' 166 | elif event.key in [K_RIGHT, K_d] and self.state == 'run': 167 | self.next_f = 'R' 168 | elif event.key in [K_DOWN, K_s] and self.state == 'run': 169 | self.next_f = 'D' 170 | elif event.key in [K_UP, K_w] and self.state == 'run': 171 | self.next_f = 'U' 172 | elif event.key in [K_k, K_l] and self.state == 'ai': 173 | if event.key == K_k and self.step_time > 0: 174 | self.step_time *= 0.9 175 | if event.key == K_l and self.step_time < 10: 176 | if self.step_time != 0: 177 | self.step_time *= 1.1 178 | else: 179 | self.step_time = 0.01 180 | if self.step_time < 0: 181 | self.step_time = 0 182 | 183 | if event.type == MOUSEBUTTONDOWN: 184 | for i in self.button_list: 185 | if i.is_click(event.pos): 186 | self.state = i.name 187 | if i.name == 'ai': 188 | i.name = 'run' 189 | i.text = '取消托管' 190 | elif i.name == 'run': 191 | i.name = 'ai' 192 | i.text = '电脑托管' 193 | break 194 | 195 | 196 | def run(): 197 | Main().start() 198 | 199 | 200 | # 按钮类 201 | class Button(pygame.sprite.Sprite): 202 | def __init__(self, name, text, xy, size=(100, 50)): 203 | pygame.sprite.Sprite.__init__(self) 204 | self.name = name 205 | self.text = text 206 | self.x, self.y = xy[0], xy[1] 207 | self.w, self.h = size 208 | self.is_show = True 209 | 210 | def is_click(self, xy): 211 | return (self.is_show and 212 | self.x <= xy[0] <= self.x + self.w and 213 | self.y <= xy[1] <= self.y + self.h) 214 | 215 | 216 | if __name__ == '__main__': 217 | run() 218 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2048GameAutoMovePython 2 | 使用python3编写2048游戏及自动玩2048 3 | 4 | 演示视频:https://www.bilibili.com/video/BV1zg411u7tH/ 5 | 6 | 使用pygame模块绘制游戏界面。 7 | # 一、2048python 8 | 2048python目录里是纯python写的。 9 | 启动方式: 10 | ```shell script 11 | cd ./2048python 12 | python3 main.py 13 | ``` 14 | 15 | # 二、2048python&c 16 | 2048python&c目录里是将2048策略算法使用c语言重写,然后编译成so文件,以提高运行速度。 17 | 18 | ## 1.编译so文件 19 | 项目中的2048.so文件是在我电脑上编译的,如果你的电脑是64位,安装的python也是64位的,且cpu什么的和我差不多,那么可以直接使用我编译的so文件。 20 | 21 | 需要安装gcc,并且得看你的python是32位还是64位,gcc也得是相应的位数。 22 | 23 | 编译: 24 | ```shell script 25 | # 进入目录 26 | cd "./2048python&c" 27 | gcc 2048ai.c -shared -o ./2048.so 28 | ``` 29 | 30 | ## 2.运行 31 | ```shell script 32 | python3 main.py 33 | ``` 34 | --------------------------------------------------------------------------------