forked from Telos4/RoboRally
implemented level generator
This commit is contained in:
parent
794c987899
commit
65f195fecf
124
gauss-turing/game/level_generator.py
Normal file
124
gauss-turing/game/level_generator.py
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
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
|
Loading…
Reference in New Issue
Block a user