Skip to content

Instantly share code, notes, and snippets.

@ide-an
Created March 14, 2014 13:24
Show Gist options
  • Save ide-an/9547586 to your computer and use it in GitHub Desktop.
Save ide-an/9547586 to your computer and use it in GitHub Desktop.
2048(http://gabrielecirulli.github.io/2048/ )のAI。JavaScriptの方はゲーム画面からゲーム状態を取り出してAIの呼出しコードに変換するブックマークレット。
DIR_UP = 0
DIR_DOWN = 1
DIR_RIGHT = 2
DIR_LEFT = 3
DIR_SENTINEL = 4
WIDTH = 4
HEIGHT = 4
def make_board():
return [[0 for i in range(WIDTH)] for j in range(HEIGHT)]
def copy_board(board):
return [[elm for elm in row] for row in board]
def update(board, direction):
new_board = make_board()
dx = [0, 0, -1, 1][direction]
dy = [1, -1, 0, 0][direction]
x = [0, 0, WIDTH-1, 0][direction]
y = [0, HEIGHT-1, 0, 0][direction]
for i in range(WIDTH):
to_x = x
to_y = y
can_merge = False
for j in range(HEIGHT):
if board[y][x] == 0:
pass
elif can_merge and 0 <= to_y-dy < HEIGHT and 0 <= to_x-dx < WIDTH and board[y][x] == new_board[to_y-dy][to_x-dx]:
new_board[to_y-dy][to_x-dx] = board[y][x] * 2
can_merge = False
else:
new_board[to_y][to_x] = board[y][x]
to_x += dx
to_y += dy
can_merge = True
x += dx
y += dy
x = [x+1, x+1, WIDTH-1, 0][direction]
y = [0, HEIGHT-1, y+1, y+1][direction]
return new_board
def same(board_a, board_b):
for i in range(HEIGHT):
for j in range(WIDTH):
if board_a[i][j] != board_b[i][j]:
return False
return True
def is_gameover(board):
return same(board, update(board, DIR_UP)) and same(board, update(board, DIR_RIGHT))
EVAL_MIN = -100000000
def evaluate(board):
if is_gameover(board):
return EVAL_MIN
num_zero = 0
for row in board:
for elm in row:
if elm == 0:
num_zero += 1
#max_ = max([max(row) for row in board])
sum_ = sum([sum(row) for row in board])
return sum_/(WIDTH*HEIGHT-num_zero)**2
TURN_PLAYER = 0
TURN_MASTER = 1
def minimax(board, turn, depth):
if depth == 0 or is_gameover(board):
return evaluate(board)
res = []
if turn == TURN_PLAYER:
for choice in range(DIR_SENTINEL):
if not(same(board, update(board, choice))):
res.append(minimax(update(board, choice), TURN_MASTER, depth-1))
return max(res)
elif turn == TURN_MASTER:
for i in range(HEIGHT):
for j in range(WIDTH):
if board[i][j] == 0:
new_board = copy_board(board)
new_board[i][j] = 2 # Actually, 2 and 4 can be put, but I ignored case of 4...
res.append(minimax(new_board, TURN_PLAYER, depth-1))
return min(res)
def ai_choice(board):
res_max = EVAL_MIN
choice_max = DIR_SENTINEL
for choice in range(DIR_SENTINEL):
if not(same(board, update(board, choice))):
res = minimax(update(board, choice), TURN_MASTER, 4)
if res > res_max:
res_max = res
choice_max = choice
print(res_max)
return choice_max
#utility
def str_to_board(s):
ss = s.split(",")
board = make_board()
for i in range(HEIGHT):
for j in range(WIDTH):
board[i][j] = int(ss[i*WIDTH+j])
return board
def dir_to_name(direction):
return [ "DIR_UP", "DIR_DOWN", "DIR_RIGHT", "DIR_LEFT"][direction]
def show_board(board):
for i in range(HEIGHT):
print(board[i])
(function(){
var board = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
for(var y=0;y<4;y++){
for(var x=0;x<4;x++){
var elm = document.querySelectorAll(".tile-position-"+(x+1)+"-"+(y+1));
if(elm.length>1){
var elm = document.querySelector(".tile-position-"+(x+1)+"-"+(y+1)+".tile-merged");
board[y*4+x] = elm.textContent - 0;
}else if(elm.length==1){
board[y*4+x] = elm[0].textContent - 0;
}
}
}
alert('dir_to_name(ai_choice(str_to_board("'+board.join()+'")))')
})()
//javascript:(function(){ var board = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; for(var y=0;y<4;y++){ for(var x=0;x<4;x++){ var elm = document.querySelectorAll(".tile-position-"+(x+1)+"-"+(y+1)); if(elm.length>1){ var elm = document.querySelector(".tile-position-"+(x+1)+"-"+(y+1)+".tile-merged"); board[y*4+x] = elm.textContent - 0; }else if(elm.length==1){ board[y*4+x] = elm[0].textContent - 0; } } } alert('dir_to_name(ai_choice(str_to_board("'+board.join()+'")))') })()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment