RoboRally/gauss-turing/game/level_generator.py

124 lines
4.1 KiB
Python

from gauss_turing import Program, Board, Command, Robot
import numpy as np
import json
levels = {
0: lambda cmds: len(cmds) <= 3,
1: lambda cmds: len(cmds) == 3
}
def rate_level(robot_path, cmds, board):
path_length = len(set(robot_path))
n_cmds = len(cmds)
n_cmd_colors = len(set([tuple(c.color) for c in cmds]))
difficulty = (path_length - 1) * (n_cmds + n_cmd_colors)
# place coins on the robot path to create a solution
if difficulty > 0:
n_coins = np.random.randint(1, min(path_length, 5)) - 1
# put one coin on last tile visited
coins = [robot_path[-1]]
# distribute other coins randomly on the path
# path without first and list tile
unique_tiles = list(set(robot_path) - {robot_path[-1]} - {robot_path[0]})
for _ in range(n_coins):
c = np.random.randint(0, len(unique_tiles))
new_coin = unique_tiles.pop(c)
coins.append(new_coin)
pass
else:
coins = []
return difficulty, coins
def generate_level(dimx, dimy, max_steps=100):
n_cmds = np.random.randint(2, 6)
assert n_cmds <= 5
# generate random board without any coins
board = Board(dimx, dimy, n_coins=0)
cmds = []
actions = list(sorted(Command.valid_actions - {'-'}))
# generate random commands
for i in range(n_cmds):
action = np.random.choice(actions)
color = Board.valid_colors[np.random.randint(len(Board.valid_colors))]
cmds.append(Command(action, color))
# generate robot at random position
rx = np.random.randint(0, dimx-1)
ry = np.random.randint(0, dimy-1)
orientation = np.random.choice(['>','v','<','^'])
r = Robot(rx, ry, orientation)
prg = Program(r, board, cmds)
continue_running = True
state = 'running'
prg_counter_old = prg.prg_counter
robot_path = [(r.x, r.y)]
step = 0
while continue_running and step < max_steps:
#print(f"prg_counter = {prg.prg_counter} - robot: {r} - state: {state}")
state = prg.step(state, check_victory=False)
robot_path.append((r.x, r.y))
stuck = prg.prg_counter == prg_counter_old
prg_counter_old = prg.prg_counter
if state == 'game_over' or stuck:
continue_running = False
step += 1
last_pos = robot_path[-1]
if not ((0 <= last_pos[0] < dimx) and (0 <= last_pos[1] < dimy)):
# remove last entry of path if robot leaves the board
robot_path.pop(-1)
difficulty, coins = rate_level(robot_path, cmds, board)
# put coins on the board
for coin in coins:
board.tiles[coin[1], coin[0]]['star'] = True
n_coins = len(coins)
return difficulty, board, n_coins, set(robot_path), (rx, ry, orientation), cmds
if __name__ == "__main__":
np.random.seed(2)
levels = {}
for i in range(100):
diff, board, n_coins, robot_path, init_robot_pos, solution = generate_level(7, 4)
if diff > 0:
print("difficulty: ", diff, "n_coins: ", n_coins, "path length: ", len(robot_path))
if diff in levels:
if n_coins > levels[diff]['n_coins'] and len(robot_path) > levels[diff]['path_length']:
levels[diff] = {'board': board, 'init_robot_pos': init_robot_pos, 'solution': solution,
'n_coins': n_coins, 'path_length': len(robot_path)}
else:
levels[diff] = {'board': board, 'init_robot_pos': init_robot_pos, 'solution': solution,
'n_coins': n_coins, 'path_length': len(robot_path)}
level_info = {}
for l, data in levels.items():
np.save(f'levels/{l}.npy', data['board'].tiles)
sol = [(cmd.action, tuple(map(int, cmd.color))) for cmd in data['solution']]
level_info[l] = {'init_robot_pos': data['init_robot_pos'], 'solution': sol,
'n_coins': int(data['n_coins']), 'path_length': int(data['path_length']),
'file': f'levels/{l}.npy'}
with open('levels/level_info.json', 'w') as f:
json.dump(level_info, f, indent=4)
pass