...
 
Commits (4)
......@@ -35,3 +35,10 @@ KEY_DOWN = "'s'"
KEY_LEFT = "'a'"
KEY_RIGHT = "'d'"
KEY_BACK = "'b'"
class Move:
Left = 'left'
Up = 'up'
Right = 'right'
Down = 'down'
import numpy as np
import logic
from constants import GRID_LEN
from constants import GRID_LEN, Move
from player import DumbPlayer
class Game:
matrix = None
commands = []
commands = {
Move.Left: logic.left,
Move.Right: logic.right,
Move.Up: logic.up,
Move.Down: logic.down,
}
def __init__(self) -> None:
super().__init__()
self.new_game()
def new_game(self):
......@@ -26,6 +33,9 @@ class Game:
def current_score(self):
return logic.game_score(self.matrix)
def fitness(self):
return np.sum(self.matrix) + 9 * self.highest_tile()
def has_lost(self):
return logic.game_state(self.matrix) == logic.STATE_LOSE
......@@ -37,20 +47,37 @@ class Game:
score = self.matrix[row][col]
return score
def _process_move(self, action):
if logic.game_state(self.matrix) == logic.STATE_PROGRESS:
self.matrix, done = action(self.matrix)
def _process_move(self, move):
try:
new_state, done = self.commands[move](self.matrix)
if done: # done = not a fully filled game matrix
self.matrix = logic.add_tile(self.matrix)
self.matrix = new_state
except KeyError:
raise Exception("Move is not one of Left, Up, Right, Down but is {}".format(str(move)))
def play_game(self, player):
self.new_game()
loop_detected = False
while not loop_detected:
previous_score = logic.total_value(self.matrix)
move = player.play(self.matrix)
self._process_move(move)
self.matrix = logic.add_tile(self.matrix)
new_score = logic.total_value(self.matrix)
loop_detected = new_score == previous_score # If there is no increase in the score, then the move did nothing
return logic.total_value(self.matrix), self.highest_tile(), self.fitness()
def up(self):
self._process_move(logic.up)
def down(self):
self._process_move(logic.down)
if __name__ == '__main__':
print("Let's play a game")
player = DumbPlayer()
def left(self):
self._process_move(logic.left)
automated_game = Game()
score = automated_game.play_game(player)
def right(self):
self._process_move(logic.right)
print("You got a total value of {}".format(score))
import multiprocessing
import random
from functools import partial
import numpy as np
from deap import base, creator, gp, tools, algorithms
from game import Game
def progn(*args):
return_values = []
for arg in args:
if isinstance(arg, str):
return_values.append(arg)
else:
return_values.append(arg())
return ' '.join(return_values)
def prog2(out1, out2):
return partial(progn, out1, out2)
def prog3(out1, out2, out3):
return partial(progn, out1, out2, out3)
def if_then_else(condition, out1, out2):
return out1() if condition() else out2()
class GamePlayer:
UP = 'up'
DOWN = 'down'
LEFT = 'left'
RIGHT = 'right'
game = None
commands = None
def __init__(self) -> None:
super().__init__()
self._reset()
def _reset(self):
self.game = Game()
self.commands = {
self.UP: self.game.up,
self.DOWN: self.game.down,
self.LEFT: self.game.left,
self.RIGHT: self.game.right,
}
def fitness(self):
return np.sum(self.game.matrix) + 9 * self.game.highest_tile()
def if_lost(self, out1, out2):
return partial(if_then_else, self.game.has_lost, out1, out2)
def play(self, routine):
self._reset()
last_score = 0
steps = 0
while not self.game.has_lost() and steps < 100:
actions = routine()
for action in actions.split(' '):
self.commands[action]()
if last_score == self.game.current_score():
steps += 1
else:
steps = 0
last_score = self.game.current_score()
pset = gp.PrimitiveSet("main", 0)
pset.addTerminal(GamePlayer.UP, name='up')
pset.addTerminal(GamePlayer.DOWN, name='down')
pset.addTerminal(GamePlayer.LEFT, name='left')
pset.addTerminal(GamePlayer.RIGHT, name='right')
# pset.addPrimitive(player.if_lost, 2)
pset.addPrimitive(prog2, 2)
pset.addPrimitive(prog3, 3)
creator.create("FitnessMax", base.Fitness, weights=(1.0, 0.5,))
creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMax)
toolbox = base.Toolbox()
# @todo
# Depth outputs
# min=1, max=2, highest tile = 512
# min=1, max=5, highest tile = 1024
# Attribute generator
toolbox.register("expr_init", gp.genFull, pset=pset, min_=1, max_=5)
# Structure initializers
toolbox.register("individual", tools.initIterate, creator.Individual,
toolbox.expr_init)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
def evalGame(individual):
# Transform the tree expression to functionnal Python code
routine = gp.compile(individual, pset)
# Run the generated routine
scores = []
highest_tiles = []
for i in range(0, 10):
player = GamePlayer()
player.play(routine)
highest_tiles.append(player.game.highest_tile())
scores.append(player.fitness())
return np.median(highest_tiles), np.median(scores)
toolbox.register("evaluate", evalGame)
toolbox.register("select", tools.selTournament, tournsize=7)
toolbox.register("mate", gp.cxOnePoint)
toolbox.register("expr_mut", gp.genFull, min_=1, max_=5)
toolbox.register("mutate", gp.mutUniform, expr=toolbox.expr_mut, pset=pset)
# pool = multiprocessing.Pool()
# toolbox.register("map", pool.map)
def stats_f(ind):
return ind.fitness.values
def stats_min(i):
return list(map(np.min, zip(*i)))
def stats_max(i):
return list(map(np.max, zip(*i)))
def stats_std(i):
return list(map(np.std, zip(*i)))
def stats_avg(i):
return list(map(np.mean, zip(*i)))
def main():
random.seed(69)
pop = toolbox.population(n=300)
hof = tools.HallOfFame(1)
stats = tools.Statistics(stats_f)
stats.register("min", stats_min)
stats.register("max", stats_max)
stats.register("std", stats_std)
stats.register("avg", stats_avg)
algorithms.eaSimple(pop, toolbox, 0.5, 0.2, 40, stats, halloffame=hof, verbose=True)
best = hof.items[0]
evalGame(best)
return pop, hof, stats
if __name__ == "__main__":
main()
import multiprocessing
import operator
import random
from functools import partial
import numpy
import numpy as np
from deap import base, creator, gp, tools, algorithms
# import logic
from constants import Move, GRID_LEN
from game import Game
from play import Automated2048, GPPlayer
def progn(*args):
for arg in args:
arg()
def prog2(out1, out2):
return partial(progn, out1, out2)
def prog3(out1, out2, out3):
return partial(progn, out1, out2, out3)
from player import GPPlayer, GridItem
def if_then_else(condition, out1, out2):
......@@ -28,7 +15,7 @@ def if_then_else(condition, out1, out2):
# class GamePlayer:
# game = Game()
# game = Game()Ò
# def _reset(self):
# self.game = Game()
......@@ -63,84 +50,135 @@ def if_then_else(condition, out1, out2):
# * Get position of highest tile
# * (integer) Constants
def grid_equals(g1, g2):
return g1.value == g2.value
def grid_lt(g1, g2):
return g1.value < g2.value
def grid_gt(g1, g2):
return g1.value > g2.value
pset = gp.PrimitiveSetTyped("main", [int] * 16, str)
pset.addTerminal('left', str, name='left')
pset.addTerminal('up', str, name='up')
pset.addTerminal('right', str, name='right')
pset.addTerminal('down', str, name='down')
pset.addTerminal(True, bool, name='bool_true')
def is_neighbour(g1, g2):
return g1.location == (g2.location - 1) or g1.location == (
g2.location + 1) or g1.location == (
g2.location - GRID_LEN) or g1.location == (
g2.location + GRID_LEN)
pset.addTerminal(12, int, name='value0')
def grid_to_grid(input):
return input
pset = gp.PrimitiveSetTyped("main", [GridItem] * (GRID_LEN * GRID_LEN), str)
pset.addTerminal(Move.Left, str, name='left')
pset.addTerminal(Move.Up, str, name='up')
pset.addTerminal(Move.Right, str, name='right')
pset.addTerminal(Move.Down, str, name='down')
pset.addTerminal(1, bool, name='bool_true')
pset.addPrimitive(grid_to_grid, [GridItem], GridItem, name='grid_item')
pset.addPrimitive(if_then_else, [bool, str, str], str, name='if_then_else')
pset.addPrimitive(operator.eq, [int, int], bool, name='eq')
pset.addPrimitive(operator.lt, [int, int], bool, name='lt')
pset.addPrimitive(operator.gt, [int, int], bool, name='gt')
pset.addPrimitive(grid_equals, [GridItem, GridItem], bool, name='eq')
pset.addPrimitive(grid_lt, [GridItem, GridItem], bool, name='lt')
pset.addPrimitive(grid_gt, [GridItem, GridItem], bool, name='gt')
pset.addPrimitive(is_neighbour, [GridItem, GridItem], bool, 'is_neighbour')
GAMES_PER_INDIVIDUAL = 11
GAMES_PER_INDIVIDUAL = 10
def evaluateIndividual(individual):
# print(individual)
# print('\n')
fn = gp.compile(individual, pset)
player = GPPlayer(fn)
game = Automated2048()
game = Game()
scores = np.array([game.play_game(player) for i in
range(GAMES_PER_INDIVIDUAL)]).transpose()
scores = [game.play_game(player) for i in range(GAMES_PER_INDIVIDUAL)]
average = sum(scores) / GAMES_PER_INDIVIDUAL
ret_val = (np.median(scores[0]), # total sum of tiles
np.max(scores[1]), # max tile
np.median(scores[2])) # fitness calc result
return [average]
return ret_val
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("FitnessMax", base.Fitness, weights=(0.2, 1.0, 0.2))
creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMax)
toolbox = base.Toolbox()
# Attribute generator
toolbox.register("expr_init", gp.genFull, pset=pset, min_=1, max_=5)
toolbox.register("expr_init", gp.genFull, pset=pset, min_=8, max_=11)
# Structure initializers
toolbox.register("individual", tools.initIterate, creator.Individual,
toolbox.expr_init)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
def evalArtificialAnt(individual):
# Transform the tree expression to functionnal Python code
routine = gp.compile(individual, pset)
# Run the generated routine
player.play(routine)
player.game.print_game()
print(player.fitness())
return player.fitness()
toolbox.register("evaluate", evaluateIndividual)
toolbox.register("select", tools.selTournament, tournsize=7)
toolbox.register("mate", gp.cxOnePoint)
toolbox.register("expr_mut", gp.genFull, min_=0, max_=2)
toolbox.register("mutate", gp.mutUniform, expr=toolbox.expr_mut, pset=pset)
pool = multiprocessing.Pool()
toolbox.register("map", pool.map)
def stats_f(ind):
return ind.fitness.values
def stats_min(i):
return list(map(np.min, zip(*i)))
def stats_max(i):
return list(map(np.max, zip(*i)))
def stats_std(i):
return list(map(np.std, zip(*i)))
def stats_avg(i):
return list(map(np.mean, zip(*i)))
def main():
# random.seed(37)
pop = toolbox.population(n=300)
hof = tools.HallOfFame(1)
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", numpy.mean)
stats.register("std", numpy.std)
stats.register("min", numpy.min)
stats.register("max", numpy.max)
algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=0.2, ngen=40, stats=stats, halloffame=hof)
stats = tools.Statistics(stats_f)
stats.register("min", stats_min)
stats.register("max", stats_max)
stats.register("std", stats_std)
stats.register("avg", stats_avg)
algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=0.2, ngen=40,
stats=stats, halloffame=hof)
# import pygraphviz as pgv
#
# g = pgv.AGraph()
# g.add_nodes_from(nodes)
# g.add_edges_from(edges)
# g.layout(prog="dot")
#
# for i in nodes:
# n = g.get_node(i)
# n.attr["label"] = labels[i]
#
# g.draw("tree.pdf")
return pop, hof, stats
if __name__ == "__main__":
main()
\ No newline at end of file
main()
from enum import Enum, unique
import logic
from constants import *
@unique
class Terminal(Enum):
Left = 1
Up = 2
Right = 3
Down = 4
class Player(object):
def __init__(self):
pass
def play(self, game):
raise NotImplementedError('Implement this in a subclass of Player!')
class DumbPlayer(object):
did_left = False
def play(self, game):
if self.did_left:
self.did_left = False
return Terminal.Down
else:
self.did_left = True
return Terminal.Left
class GPPlayer(object):
def __init__(self, individual_fn):
self.individual_fn = individual_fn
def play(self, game):
game = [cell for row in game for cell in row]
args = {"ARG" + str(i): game[i] for i in range(16)}
move_str = self.individual_fn(**args)
if move_str == "left":
return Terminal.Left
elif move_str == "up":
return Terminal.Up
elif move_str == "right":
return Terminal.Right
elif move_str == "down":
return Terminal.Down
class Automated2048(object):
state = None
def __init__(self):
pass
def play_game(self, player):
self._new_game()
loop_detected = False
while not loop_detected:
previous_score = logic.total_value(self.state)
move = player.play(self.state)
self.process_move(move)
self.state = logic.add_tile(self.state)
new_score = logic.total_value(self.state)
loop_detected = new_score == previous_score # If there is no increase in the score, then the move did nothing
return logic.total_value(self.state)
def _new_game(self):
self.state = logic.new_game(GRID_LEN)
self.state = logic.add_tile(self.state)
self.state = logic.add_tile(self.state)
def process_move(self, move):
if move == Terminal.Left:
new_state, _ = logic.left(self.state)
elif move == Terminal.Up:
new_state, _ = logic.up(self.state)
elif move == Terminal.Right:
new_state, _ = logic.right(self.state)
elif move == Terminal.Down:
new_state, _ = logic.down(self.state)
else:
raise Exception("Move is not one of Left, Up, Right, Down but is {}".format(str(move)))
self.state = new_state
def print_game(self):
for row in self.state:
row_line = ''
for value in row:
row_line += f' {value} '
print(row_line)
if __name__ == '__main__':
print("Let's play a game")
player = DumbPlayer()
automated_game = Automated2048()
score = automated_game.play_game(player)
print("You got a total value of {}".format(score))
from constants import Move, GRID_LEN
class GridItem:
location = None
value = None
def __init__(self, location, value) -> None:
super().__init__()
self.location = location
self.value = value
class Player(object):
def __init__(self):
pass
def play(self, game):
raise NotImplementedError('Implement this in a subclass of Player!')
class DumbPlayer(object):
did_left = False
def play(self, game):
if self.did_left:
self.did_left = False
return Move.Down
else:
self.did_left = True
return Move.Left
class GPPlayer(object):
def __init__(self, individual_fn):
self.individual_fn = individual_fn
def play(self, game):
game = [cell for row in game for cell in row]
args = {"ARG" + str(i): GridItem(i, game[i])
for i in range(GRID_LEN * GRID_LEN)}
return self.individual_fn(**args)