Compare commits
7 Commits
ce720a92b2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d6e2fc7be9 | |||
| 3d2f98d56e | |||
| 0fd490b97c | |||
| 104297e88b | |||
| 104c6fac63 | |||
| a7a3d973a3 | |||
| f56b8c1e9c |
+57
-10
@@ -22,7 +22,7 @@ any map when no such map exists.
|
|||||||
|
|
||||||
The score is from 0 to 100:
|
The score is from 0 to 100:
|
||||||
|
|
||||||
`40 * coin_progress + 40 * distance_progress + 20 * won`
|
`50 * coin_progress + 10 * distance_progress + 40 * won`
|
||||||
|
|
||||||
where `coin_progress` is collected coins divided by the map's total coins,
|
where `coin_progress` is collected coins divided by the map's total coins,
|
||||||
`distance_progress` is progress from the spawn point to the finish, and `won`
|
`distance_progress` is progress from the spawn point to the finish, and `won`
|
||||||
@@ -37,21 +37,29 @@ are requested separately on every turn.
|
|||||||
```
|
```
|
||||||
|
|
||||||
Every model gets a separate `Game` instance and can run in parallel. Valid
|
Every model gets a separate `Game` instance and can run in parallel. Valid
|
||||||
requests. Valid moves are `left_down`, `left`, `left_up`, `up`, `right_up`,
|
moves are `left_down`, `left`, `left_up`, `up`, `right_up`,
|
||||||
`right`, and `right_down`. Send `null` for a model when it has no more moves;
|
`right`, and `right_down`. Send `null` for a model when it has no more moves;
|
||||||
that model is stopped for the current generation.
|
that model is stopped for the current generation.
|
||||||
|
|
||||||
The response identifies the selected map and returns the initial state:
|
The initial response is a `move_request`. It contains the selected map and the
|
||||||
|
initial state of every model. The map is sent only in this initial response;
|
||||||
|
clients should cache it for the rest of the simulation.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"ok":true,
|
"ok":true,
|
||||||
"type":"move_request",
|
"type":"move_request",
|
||||||
"map":{"name":"default","difficulty":30.0,"layout":[["stone", "stone"]]},
|
"map":{"name":"default","difficulty":30.0,"layout":[["stone", "stone"]]},
|
||||||
"results":{"model-a":{"won":false,"coins":0,"moves":0,"position":[1,4],"finished":false},"model-b":{"won":false,"coins":0,"moves":0,"position":[1,4],"finished":false}}
|
"results":{"model-a":{"won":false,"coins":0,"moves":0,"position":[1,4],"finished":false,"changes":[]},"model-b":{"won":false,"coins":0,"moves":0,"position":[1,4],"finished":false,"changes":[]}}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
After receiving this response, send one move for every model:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"type":"step","moves":{"model-a":"right","model-b":"right_up"}}
|
||||||
|
```
|
||||||
|
|
||||||
## Advance one turn
|
## Advance one turn
|
||||||
|
|
||||||
The client must send exactly one move for every model on each turn. The server
|
The client must send exactly one move for every model on each turn. The server
|
||||||
@@ -62,10 +70,49 @@ predict the next moves.
|
|||||||
{"type":"step","moves":{"model-a":"right","model-b":null}}
|
{"type":"step","moves":{"model-a":"right","model-b":null}}
|
||||||
```
|
```
|
||||||
|
|
||||||
The response is `move_request` with the selected map and updated per-model states, asking the
|
The response is another `move_request` containing the updated state and score
|
||||||
client for the next move of every model. When every model has finished, the
|
for every model. Each model also receives `changes`, a cumulative list of
|
||||||
response type becomes `simulation_result` and includes the best model's
|
blocks that differ from the base map. For example, a collected coin is sent as
|
||||||
server-computed `score`, which is used to choose the next training map.
|
`{"position":[4,7],"type":null}`. Unchanged turns return an empty list. It
|
||||||
|
asks the client for the next move of every unfinished model. A model that has
|
||||||
|
no more moves can send `null`:
|
||||||
|
|
||||||
The result score is `1000` for a win, plus `10` per coin, minus the number of
|
```json
|
||||||
moves. Malformed requests receive `{"ok":false,"error":"..."}`.
|
{"type":"step","moves":{"model-a":"right","model-b":null}}
|
||||||
|
```
|
||||||
|
|
||||||
|
When every model has finished, the response type becomes `simulation_result`.
|
||||||
|
It contains the final state and score for every model, plus the best score used
|
||||||
|
to choose the next training map:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ok":true,
|
||||||
|
"type":"simulation_result",
|
||||||
|
"results":{
|
||||||
|
"model-a":{
|
||||||
|
"won":true,
|
||||||
|
"coins":3,
|
||||||
|
"moves":18,
|
||||||
|
"position":[23,1],
|
||||||
|
"finished":true,
|
||||||
|
"changes":[{"position":[4,7],"type":null}],
|
||||||
|
"score":92
|
||||||
|
},
|
||||||
|
"model-b":{
|
||||||
|
"won":false,
|
||||||
|
"coins":1,
|
||||||
|
"moves":7,
|
||||||
|
"position":[8,4],
|
||||||
|
"finished":true,
|
||||||
|
"changes":[],
|
||||||
|
"score":24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"score":92
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The score is calculated by the server using the weights described above. The
|
||||||
|
top-level `score` is the best model score for the completed generation.
|
||||||
|
Malformed requests receive `{"ok":false,"type":"error","error":"..."}`.
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Améliorations
|
||||||
|
|
||||||
|
- Pénaliser les mouvements impossibles : intéressant, même pour l'algo génétique, pourrait optimiser le genome
|
||||||
|
- Pénaliser le fait de terminer la simulation sur une case où l'on est déjà passé ou pénaliser de revenir sur une case où l'on est passé
|
||||||
@@ -24,6 +24,7 @@ of moves to be executed in a single game simulation, based on the map min_moves
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import socketserver
|
import socketserver
|
||||||
@@ -43,12 +44,19 @@ from game.mapSet import MapSet
|
|||||||
class AiHttpGameHandler(GameHandler):
|
class AiHttpGameHandler(GameHandler):
|
||||||
DEFAULT_HOST = "127.0.0.1"
|
DEFAULT_HOST = "127.0.0.1"
|
||||||
DEFAULT_PORT = 8765
|
DEFAULT_PORT = 8765
|
||||||
VALIDATION_DIFFICULTY = 70
|
VALIDATION_DIFFICULTY = 90
|
||||||
REPLAY_FRAME_DELAY = 0.1
|
REPLAY_FRAME_DELAY = 0.1
|
||||||
# Scoring weights TOTAL MUST BE 100
|
# Scoring weights TOTAL MUST BE 100
|
||||||
COINS_WEIGHT = 40
|
COINS_WEIGHT = 50
|
||||||
DISTANCE_WEIGHT = 20
|
STEPS_WEIGHT = 20
|
||||||
WIN_WEIGHT = 40
|
DISTANCE_WEIGHT = 10
|
||||||
|
WIN_WEIGHT = 20
|
||||||
|
PLAY_ALL_MAPS_PER_GENERATION = False # Are all maps played during a generation or only MAPS_PER_GENERATION maps per generation are played
|
||||||
|
MAPS_PER_GENERATION = 3 # Evaluate across x maps per generation. Only used if PLAY_ALL_MAPS_PER_GENERATION is False. If True, all maps are played in a generation.
|
||||||
|
MAP_PASS_MIN_SCORE = 20 # Minimum score to consider a map passed. Only used if PLAY_ALL_MAPS_PER_GENERATION is False. If True, all maps are played in a generation.
|
||||||
|
MAP_DIFFICULTY_VARIANCE = 5 # Lower value will make the map selection more variable, higher value will make the map selection more consistent with the current performance. Only used if PLAY_ALL_MAPS_PER_GENERATION is False. A value too high may produce no evolving behavior
|
||||||
|
CONSISTENCY_PENALTY_WEIGHT = 0.4 # Penalty for inconsistent map scores
|
||||||
|
MINIMUM_PERFORMANCE_DELTA = 0.05 # Minimum performance delta to add every generation. This ensures that the map will always change even if the ai is stuck in a local optimum. Only used if PLAY_ALL_MAPS_PER_GENERATION is False. A value too high may produce no evolving behavior
|
||||||
|
|
||||||
def __init__(self, host: str | None = None, port: int | None = None,
|
def __init__(self, host: str | None = None, port: int | None = None,
|
||||||
max_workers: int | None = None):
|
max_workers: int | None = None):
|
||||||
@@ -62,6 +70,10 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
game_map for game_map in self.map_set.maps
|
game_map for game_map in self.map_set.maps
|
||||||
if game_map.difficulty >= self.VALIDATION_DIFFICULTY
|
if game_map.difficulty >= self.VALIDATION_DIFFICULTY
|
||||||
] or list(self.map_set.maps)
|
] or list(self.map_set.maps)
|
||||||
|
|
||||||
|
if (self.COINS_WEIGHT + self.DISTANCE_WEIGHT + self.STEPS_WEIGHT + self.WIN_WEIGHT) != 100:
|
||||||
|
raise ValueError("Scoring weights must sum to 100.")
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
handler = self
|
handler = self
|
||||||
|
|
||||||
@@ -73,6 +85,10 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
"best_run": None,
|
"best_run": None,
|
||||||
"replay_condition": Condition(),
|
"replay_condition": Condition(),
|
||||||
"client_connected": True,
|
"client_connected": True,
|
||||||
|
"stagnant_generations": 0,
|
||||||
|
"map_round": 0,
|
||||||
|
"cumulative_scores": {},
|
||||||
|
"last_results": {}
|
||||||
}
|
}
|
||||||
replay_thread = Thread(target=handler._replay_best_runs, args=(session,))
|
replay_thread = Thread(target=handler._replay_best_runs, args=(session,))
|
||||||
replay_thread.start()
|
replay_thread.start()
|
||||||
@@ -81,12 +97,9 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
for raw_request in self.rfile:
|
for raw_request in self.rfile:
|
||||||
try:
|
try:
|
||||||
request = json.loads(raw_request)
|
request = json.loads(raw_request)
|
||||||
# print(f"AI request received: {request.get('type')} : {request}", flush=True)
|
|
||||||
response = handler._handle_request(request, session)
|
response = handler._handle_request(request, session)
|
||||||
except (json.JSONDecodeError, TypeError, ValueError, KeyError) as error:
|
except (json.JSONDecodeError, TypeError, ValueError, KeyError) as error:
|
||||||
response = {"ok": False, "type": "error", "error": str(error)}
|
response = {"ok": False, "type": "error", "error": str(error)}
|
||||||
# print(f"AI request error: {error}", flush=True)
|
|
||||||
# print(f"AI response sent: {response.get('type')}", flush=True)
|
|
||||||
compact_response = json.dumps(response, separators=(",", ":"))
|
compact_response = json.dumps(response, separators=(",", ":"))
|
||||||
self.wfile.write((compact_response + "\n").encode("utf-8"))
|
self.wfile.write((compact_response + "\n").encode("utf-8"))
|
||||||
self.wfile.flush()
|
self.wfile.flush()
|
||||||
@@ -128,15 +141,30 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
raise ValueError("Simulation requests must belong to a TCP session.")
|
raise ValueError("Simulation requests must belong to a TCP session.")
|
||||||
if session.get("simulations"):
|
if session.get("simulations"):
|
||||||
raise ValueError("A simulation is already active in this session.")
|
raise ValueError("A simulation is already active in this session.")
|
||||||
session["generation"] = session.get("generation", 0) + 1
|
|
||||||
selected_map = self._select_map(session.get("performance", 0.0), validation)
|
|
||||||
session["last_result"] = None
|
|
||||||
|
|
||||||
# print(
|
# Initialize generation round tracking and select 3 maps upfront if starting fresh
|
||||||
# f"Selected map for simulation: {selected_map.name} "
|
if session.get("map_round", 0) == 0:
|
||||||
# f"(difficulty: {selected_map.difficulty})",
|
session["generation"] = session.get("generation", 0) + 1
|
||||||
# flush=True,
|
session["map_scores"] = {model.get("id"): [] for model in models}
|
||||||
# )
|
|
||||||
|
# Pre-select the 3 maps for this entire generation based on current performance
|
||||||
|
if validation:
|
||||||
|
session["generation_maps"] = [
|
||||||
|
random.choice(self.validation_maps)
|
||||||
|
for _ in range(self.MAPS_PER_GENERATION)
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
perf = session.get("performance", 0.0)
|
||||||
|
if self.PLAY_ALL_MAPS_PER_GENERATION:
|
||||||
|
session["generation_maps"] = self.map_set.get_maps()
|
||||||
|
else:
|
||||||
|
session["generation_maps"] = sorted(self.map_set.get_maps_by_difficulty(round(perf), self.MAPS_PER_GENERATION), key=lambda m: m.difficulty)
|
||||||
|
|
||||||
|
# Fetch the pre-selected map for the current round
|
||||||
|
map_round = session.get("map_round", 0)
|
||||||
|
selected_map = session["generation_maps"][map_round]
|
||||||
|
|
||||||
|
session["last_result"] = None
|
||||||
|
|
||||||
simulations = {}
|
simulations = {}
|
||||||
for model in models:
|
for model in models:
|
||||||
@@ -146,9 +174,7 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
if model_id in simulations:
|
if model_id in simulations:
|
||||||
raise ValueError(f"Duplicate model id '{model_id}'.")
|
raise ValueError(f"Duplicate model id '{model_id}'.")
|
||||||
simulation = AiSimulation(selected_map)
|
simulation = AiSimulation(selected_map)
|
||||||
# print(f"Starting simulation for model '{model_id}'", flush=True)
|
|
||||||
simulation.start()
|
simulation.start()
|
||||||
# print(f"Model '{model_id}' is ready for its first move", flush=True)
|
|
||||||
simulations[model_id] = simulation
|
simulations[model_id] = simulation
|
||||||
|
|
||||||
session["simulations"] = simulations
|
session["simulations"] = simulations
|
||||||
@@ -165,7 +191,6 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
raise ValueError("No simulation is active. Send a 'simulate' request first.")
|
raise ValueError("No simulation is active. Send a 'simulate' request first.")
|
||||||
if not session.get("simulations"):
|
if not session.get("simulations"):
|
||||||
if session.get("last_result") is not None:
|
if session.get("last_result") is not None:
|
||||||
print("Repeated step after simulation completion; returning final result", flush=True)
|
|
||||||
return session["last_result"]
|
return session["last_result"]
|
||||||
raise ValueError("No simulation is active. Send a 'simulate' request first.")
|
raise ValueError("No simulation is active. Send a 'simulate' request first.")
|
||||||
moves = request.get("moves")
|
moves = request.get("moves")
|
||||||
@@ -189,7 +214,7 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
simulation = simulations[model_id]
|
simulation = simulations[model_id]
|
||||||
# print(f"Starting step for model '{model_id}'", flush=True)
|
# print(f"Starting step for model '{model_id}'", flush=True)
|
||||||
result = simulation.step(move)
|
result = simulation.step(move)
|
||||||
result["score"] = self._score_result(simulation, result)
|
result["score"] = self._score_simulation(simulation, result)
|
||||||
# print(f"Finished step for model '{model_id}'", flush=True)
|
# print(f"Finished step for model '{model_id}'", flush=True)
|
||||||
return model_id, result
|
return model_id, result
|
||||||
|
|
||||||
@@ -198,23 +223,70 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
# print(f"Completed step for models: {list(moves)}", flush=True)
|
# print(f"Completed step for models: {list(moves)}", flush=True)
|
||||||
all_finished = all(result["finished"] for result in results.values())
|
all_finished = all(result["finished"] for result in results.values())
|
||||||
response = {"ok": True, "type": "move_request", "results": results}
|
response = {"ok": True, "type": "move_request", "results": results}
|
||||||
|
|
||||||
if all_finished:
|
if all_finished:
|
||||||
response["type"] = "simulation_result"
|
session["map_round"] += 1
|
||||||
best_score = max(result["score"] for result in results.values())
|
|
||||||
best_model_id = max(results, key=lambda model_id: results[model_id]["score"])
|
# Accumulate scores for this map round
|
||||||
response["score"] = best_score
|
for model_id, res in results.items():
|
||||||
session["performance"] = best_score
|
session["map_scores"][model_id].append(res["score"])
|
||||||
session["last_result"] = response
|
|
||||||
session["simulations"] = None
|
# Check if we have completed all maps for this generation
|
||||||
session["best_run"] = (
|
if session["map_round"] < len(session["generation_maps"]):
|
||||||
deepcopy(session["map"]),
|
# More maps remaining in this generation! Tell client to simulate next map.
|
||||||
list(simulations[best_model_id].moves),
|
response["type"] = "map_round_complete"
|
||||||
session["generation"],
|
response["map_round"] = session["map_round"]
|
||||||
)
|
session["simulations"] = None
|
||||||
replay_condition = session.get("replay_condition")
|
session["last_result"] = response
|
||||||
if replay_condition is not None:
|
else:
|
||||||
with replay_condition:
|
# All maps completed -> finalize generation results (Average scores)
|
||||||
replay_condition.notify_all()
|
response["type"] = "simulation_result"
|
||||||
|
final_results = {}
|
||||||
|
|
||||||
|
# Calculate final scores for each model based on their score on every map
|
||||||
|
for model_id, scores in session["map_scores"].items():
|
||||||
|
# Calculate Mean Squared Error from perfection (100)
|
||||||
|
squared_errors = [(100.0 - score) ** 2 for score in scores]
|
||||||
|
mse = sum(squared_errors) / len(squared_errors)
|
||||||
|
rmse = math.sqrt(mse)
|
||||||
|
|
||||||
|
# Final score penalizes variance/drops sharply based on RMSE
|
||||||
|
final_score = round(max(0.0, min(100.0, 100.0 - rmse)), 2)
|
||||||
|
|
||||||
|
# Attach final structural properties from the last results set
|
||||||
|
final_results[model_id] = deepcopy(results[model_id])
|
||||||
|
final_results[model_id]["score"] = final_score
|
||||||
|
|
||||||
|
response["results"] = final_results
|
||||||
|
best_score = max(res["score"] for res in final_results.values())
|
||||||
|
best_model_id = max(final_results, key=lambda mid: final_results[mid]["score"])
|
||||||
|
response["score"] = best_score
|
||||||
|
|
||||||
|
# --- Performance Update ---
|
||||||
|
# Average score of the 90th percentile of the final results
|
||||||
|
sorted_scores = sorted((res["score"] for res in final_results.values()), reverse=True)
|
||||||
|
top_90_count = max(1, round(len(sorted_scores) * 0.9))
|
||||||
|
top_90_scores = sorted_scores[:top_90_count]
|
||||||
|
avg_top_90_score = sum(top_90_scores) / len(top_90_scores)
|
||||||
|
performance_delta = (avg_top_90_score - self.MAP_PASS_MIN_SCORE) / self.MAP_DIFFICULTY_VARIANCE
|
||||||
|
performance_delta = max(self.MINIMUM_PERFORMANCE_DELTA, performance_delta)
|
||||||
|
session["performance"] = max(0.0, session.get("performance", 0.0) + performance_delta)
|
||||||
|
# print(f"Generation {session['generation']} completed on difficulty {map_difficulty}. Best score: {best_score:.2f}. Updated performance: {session['performance']:.2f}", flush=True)
|
||||||
|
|
||||||
|
# Reset round counter for the next generation
|
||||||
|
session["map_round"] = 0
|
||||||
|
session["last_result"] = response
|
||||||
|
session["simulations"] = None
|
||||||
|
session["best_run"] = (
|
||||||
|
deepcopy(session["map"]),
|
||||||
|
list(simulations[best_model_id].moves),
|
||||||
|
session["generation"],
|
||||||
|
session["performance"],
|
||||||
|
)
|
||||||
|
replay_condition = session.get("replay_condition")
|
||||||
|
if replay_condition is not None:
|
||||||
|
with replay_condition:
|
||||||
|
replay_condition.notify_all()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def _replay_best_runs(self, session: dict) -> None:
|
def _replay_best_runs(self, session: dict) -> None:
|
||||||
@@ -231,7 +303,7 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
return
|
return
|
||||||
self._show_best_run(*best_run)
|
self._show_best_run(*best_run)
|
||||||
|
|
||||||
def _show_best_run(self, game_map, moves: list[str], generation: int) -> None:
|
def _show_best_run(self, game_map, moves: list[str], generation: int, performance: float) -> None:
|
||||||
game = Game(game_map, GameState())
|
game = Game(game_map, GameState())
|
||||||
remaining_moves = iter(moves)
|
remaining_moves = iter(moves)
|
||||||
|
|
||||||
@@ -247,11 +319,15 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
frame_lines = []
|
frame_lines = []
|
||||||
for index, line in enumerate(game_string.splitlines()):
|
for index, line in enumerate(game_string.splitlines()):
|
||||||
if index == 0:
|
if index == 0:
|
||||||
line += "\tCoins: " + str(game.gameState.coins)
|
|
||||||
elif index == 1:
|
|
||||||
line += "\tMoves: " + str(game.gameState.movesCount)
|
|
||||||
elif index == 2:
|
|
||||||
line += "\tGeneration: " + str(generation)
|
line += "\tGeneration: " + str(generation)
|
||||||
|
elif index == 1:
|
||||||
|
line += "\tPerformance: " + str(round(performance, 2))
|
||||||
|
elif index == 2:
|
||||||
|
line += "\tCoins: " + str(game.gameState.coins)
|
||||||
|
elif index == 3:
|
||||||
|
line += "\tMoves: " + str(game.gameState.movesCount)
|
||||||
|
elif index == 4:
|
||||||
|
line += "\tMove: " + moves[game.gameState.movesCount - 1] if len(moves) >= game.gameState.movesCount else "\tMove: None"
|
||||||
frame_lines.append(line)
|
frame_lines.append(line)
|
||||||
print("\033[2J\033[H\n\n\n" + "\n".join(frame_lines), flush=True)
|
print("\033[2J\033[H\n\n\n" + "\n".join(frame_lines), flush=True)
|
||||||
sleep(self.REPLAY_FRAME_DELAY)
|
sleep(self.REPLAY_FRAME_DELAY)
|
||||||
@@ -264,20 +340,34 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
results = {}
|
results = {}
|
||||||
for model_id, simulation in simulations.items():
|
for model_id, simulation in simulations.items():
|
||||||
result = simulation.result()
|
result = simulation.result()
|
||||||
result["score"] = self._score_result(simulation, result)
|
result["score"] = self._score_simulation(simulation, result)
|
||||||
results[model_id] = result
|
results[model_id] = result
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def _select_map(self, performance: float, validation: bool):
|
def _select_map(self, performance: float, validation: bool):
|
||||||
if validation:
|
if validation:
|
||||||
return random.choice(self.validation_maps)
|
return random.choice(self.validation_maps)
|
||||||
return self.map_set.get_maps_by_difficulty(round(performance), 1)[0]
|
maps = self.map_set.get_maps_by_difficulty(round(performance), 2)
|
||||||
|
return random.choice(maps)
|
||||||
|
|
||||||
def _score_result(self, simulation: AiSimulation, result: dict) -> int:
|
def _score_simulation(self, simulation: AiSimulation, result: dict) -> int:
|
||||||
if (self.COINS_WEIGHT + self.DISTANCE_WEIGHT + self.WIN_WEIGHT) != 100:
|
|
||||||
raise ValueError("Scoring weights must sum to 100.")
|
|
||||||
|
|
||||||
game_map = simulation.game.map
|
game_map = simulation.game.map
|
||||||
|
raw_score = self._score_result(game_map, result)
|
||||||
|
max_score = self._get_map_max_score(game_map)
|
||||||
|
if max_score == 0:
|
||||||
|
return 0
|
||||||
|
percentage = (raw_score / max_score) * 100.0
|
||||||
|
return round(max(0.0, min(100.0, percentage)))
|
||||||
|
|
||||||
|
def _get_map_max_score(self, game_map: Map) -> int:
|
||||||
|
result = {}
|
||||||
|
result["position"] = game_map.get_first_block_type_position("finish")
|
||||||
|
result["coins"] = game_map.count_blocks_of_type("coin")
|
||||||
|
result["won"] = True
|
||||||
|
result["moves"] = game_map.min_moves
|
||||||
|
return self._score_result(game_map, result)
|
||||||
|
|
||||||
|
def _score_result(self, game_map: Map, result: dict) -> int:
|
||||||
finish = game_map.get_first_block_type_position("finish")
|
finish = game_map.get_first_block_type_position("finish")
|
||||||
spawn = game_map.get_first_block_type_position("spawn_point")
|
spawn = game_map.get_first_block_type_position("spawn_point")
|
||||||
total_coins = game_map.count_blocks_of_type("coin")
|
total_coins = game_map.count_blocks_of_type("coin")
|
||||||
@@ -290,11 +380,13 @@ class AiHttpGameHandler(GameHandler):
|
|||||||
distance_progress = 1.0 if start_distance == 0 else max(
|
distance_progress = 1.0 if start_distance == 0 else max(
|
||||||
0.0, min(1.0, 1.0 - remaining_distance / start_distance)
|
0.0, min(1.0, 1.0 - remaining_distance / start_distance)
|
||||||
)
|
)
|
||||||
|
steps_progress = 0.0 if result["moves"] == 0 else min(1.0, 1.0 - result["moves"] / game_map.min_moves)
|
||||||
|
|
||||||
coin_progress = 0.0 if total_coins == 0 else min(1.0, result["coins"] / total_coins)
|
coin_progress = 0.0 if total_coins == 0 else min(1.0, result["coins"] / total_coins)
|
||||||
return round(
|
return round(
|
||||||
self.COINS_WEIGHT * coin_progress
|
self.COINS_WEIGHT * coin_progress
|
||||||
+ self.DISTANCE_WEIGHT * distance_progress
|
+ self.DISTANCE_WEIGHT * distance_progress
|
||||||
|
+ self.STEPS_WEIGHT * steps_progress
|
||||||
+ self.WIN_WEIGHT * result["won"]
|
+ self.WIN_WEIGHT * result["won"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class AiSimulation:
|
|||||||
|
|
||||||
def __init__(self, game_map: Map):
|
def __init__(self, game_map: Map):
|
||||||
self.game = Game(deepcopy(game_map), GameState())
|
self.game = Game(deepcopy(game_map), GameState())
|
||||||
|
self._base_map_types = self.game.map.to_type_dict()
|
||||||
min_moves = self.game.map.get_min_moves()
|
min_moves = self.game.map.get_min_moves()
|
||||||
self.max_moves = min_moves * 2 if min_moves is not None else None
|
self.max_moves = min_moves * 2 if min_moves is not None else None
|
||||||
self._condition = Condition()
|
self._condition = Condition()
|
||||||
@@ -65,8 +66,18 @@ class AiSimulation:
|
|||||||
"moves": state.movesCount,
|
"moves": state.movesCount,
|
||||||
"position": list(state.get_player_position()),
|
"position": list(state.get_player_position()),
|
||||||
"finished": state.stopGame,
|
"finished": state.stopGame,
|
||||||
|
"changes": self._get_map_changes(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _get_map_changes(self) -> list[dict]:
|
||||||
|
current_map_types = self.game.map.to_type_dict()
|
||||||
|
changes = []
|
||||||
|
for y, row in enumerate(current_map_types):
|
||||||
|
for x, block_type in enumerate(row):
|
||||||
|
if block_type != self._base_map_types[y][x]:
|
||||||
|
changes.append({"position": [x, y], "type": block_type})
|
||||||
|
return changes
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
self.stop_for_generation()
|
self.stop_for_generation()
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -71,6 +71,12 @@ class Map:
|
|||||||
def to_type_dict(self):
|
def to_type_dict(self):
|
||||||
"""Returns a matrix containing block types and None for empty spaces."""
|
"""Returns a matrix containing block types and None for empty spaces."""
|
||||||
return [
|
return [
|
||||||
[block.type if block is not None else None for block in row]
|
[self._visible_block_type(block) for block in row]
|
||||||
for row in self.layout
|
for row in self.layout
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _visible_block_type(block):
|
||||||
|
if block is None or getattr(block, "collected", False):
|
||||||
|
return None
|
||||||
|
return block.type
|
||||||
@@ -21,6 +21,9 @@ class MapSet:
|
|||||||
raise IndexError("Map index out of range.")
|
raise IndexError("Map index out of range.")
|
||||||
return self.maps[index]
|
return self.maps[index]
|
||||||
|
|
||||||
|
def get_maps(self):
|
||||||
|
return sorted(self.maps, key=lambda m: m.difficulty)
|
||||||
|
|
||||||
def get_maps_by_difficulty(self, desired_difficulty: int, num_maps: int):
|
def get_maps_by_difficulty(self, desired_difficulty: int, num_maps: int):
|
||||||
"""
|
"""
|
||||||
Returns a list of maps with difficulty levels as close as possible to the desired difficulty level.
|
Returns a list of maps with difficulty levels as close as possible to the desired difficulty level.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ layout = """
|
|||||||
## ° ###^########
|
## ° ###^########
|
||||||
## # ##########
|
## # ##########
|
||||||
#S ##^#####
|
#S ##^#####
|
||||||
#########
|
##########
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Ya moyen d'optimiser le trajet sur les surfaces plates en faisant des sauts au lieu de marcher normalement
|
# Ya moyen d'optimiser le trajet sur les surfaces plates en faisant des sauts au lieu de marcher normalement
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
difficulty = 85
|
||||||
|
min_moves = 40
|
||||||
|
layout = """
|
||||||
|
#####
|
||||||
|
###### ########
|
||||||
|
## ####
|
||||||
|
### °E##
|
||||||
|
### ° °#####
|
||||||
|
##°°° ° °° ° ° °## ######
|
||||||
|
#°####### ## # # ### ######
|
||||||
|
# #° °°° ##
|
||||||
|
## ##°° ° ##°° ^###
|
||||||
|
# ###°° ####° ##
|
||||||
|
# #####^#####°° ##
|
||||||
|
# #####°####° #
|
||||||
|
# ° °°°°# #
|
||||||
|
# ° °°# #####^^##
|
||||||
|
## ° °###^########
|
||||||
|
## °# ##########
|
||||||
|
#S °##^#####
|
||||||
|
##########
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Big_peanut avec des pièces partout (HAARRRRRR !)
|
||||||
@@ -14,6 +14,56 @@ layout = """
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#000000000000000000000000000000KKKKKKKKKKKKKKKKKKXXKKKKKXXXXNWMMNNXXXNNNNNNNNWWWWWMMMMMMMMMMMMMMMMMMM
|
#000000000000000000000000000000KKKKKKKKKKKKKKKKKKXXKKKKKXXXXNWMMNNXXXNNNNNNNNWWWWWMMMMMMMMMMMMMMMMMMM
|
||||||
#0000000000000000000000000000000KKKKKKKKKKKKKKKKKXWWXKKKKXXXNNWNXXXXXXNNNNNNNMWWWMMMMMMMMMMMMMMMMMMMM
|
#0000000000000000000000000000000KKKKKKKKKKKKKKKKKXWWXKKKKXXXNNWNXXXXXXNNNNNNNMWWWMMMMMMMMMMMMMMMMMMMM
|
||||||
#00000000000000000000000000000000KKKKKKKKKKKKKKKKXMMNKKKKXXXXXXXXXXXXXXNNNNNNNWWWWMMMMMMMMMMMMMMMMMM0
|
#00000000000000000000000000000000KKKKKKKKKKKKKKKKXMMNKKKKXXXXXXXXXXXXXXNNNNNNNWWWWMMMMMMMMMMMMMMMMMM0
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
difficulty = 20
|
difficulty = 30
|
||||||
min_moves = 28
|
min_moves = 28
|
||||||
layout = """
|
layout = """
|
||||||
# #
|
# ° ° ° ° ° ° ° ° ° ° ° #
|
||||||
#E #
|
#E #
|
||||||
##########################°^#
|
##########################°^#
|
||||||
# #°#°# #°#°# #°#°#°###
|
# #°#°# #°#°# #°#°#°###
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
difficulty = 10
|
difficulty = 25
|
||||||
min_moves = 12
|
min_moves = 12
|
||||||
layout = """
|
layout = """
|
||||||
########
|
########
|
||||||
@@ -8,7 +8,7 @@ layout = """
|
|||||||
# ##
|
# ##
|
||||||
# ###
|
# ###
|
||||||
# ##
|
# ##
|
||||||
# ## ##
|
# °## ##
|
||||||
# ### ##
|
# ### ##
|
||||||
# ° #
|
# ° #
|
||||||
# ^ #
|
# ^ #
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
difficulty = 13
|
difficulty = 20
|
||||||
min_moves = 24
|
min_moves = 24
|
||||||
layout = """
|
layout = """
|
||||||
E##
|
E##
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
difficulty = 15
|
difficulty = 10
|
||||||
min_moves = 14
|
min_moves = 14
|
||||||
layout = """
|
layout = """
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
difficulty = 60
|
difficulty = 70
|
||||||
min_moves = 6
|
min_moves = 6
|
||||||
layout = """
|
layout = """
|
||||||
|
# #
|
||||||
# S #
|
# S #
|
||||||
# ##### #
|
# ##### #
|
||||||
# #
|
# #
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
difficulty = 0
|
difficulty = 5
|
||||||
min_moves = 11
|
min_moves = 11
|
||||||
layout = """
|
layout = """
|
||||||
# #
|
# #
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
difficulty = 7
|
||||||
|
min_moves = 11
|
||||||
|
layout = """
|
||||||
|
# #
|
||||||
|
#E S#
|
||||||
|
#########################
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Teach to not only go right
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
difficulty = 40
|
difficulty = 40
|
||||||
min_moves = 6
|
min_moves = 8
|
||||||
layout = """
|
layout = """
|
||||||
#^ ^#
|
#^ ^#
|
||||||
#^ S^#
|
#^ S^#
|
||||||
#^ #^#
|
#^ #^#
|
||||||
#^ ^#
|
#^ ^#
|
||||||
#^# ^#
|
#^# ^#
|
||||||
#^ ^#
|
#^ °^#
|
||||||
#^ #^#
|
#^ #^#
|
||||||
#^# ^#
|
#^# ^#
|
||||||
#^ #^#
|
#^ #^#
|
||||||
#^# ^#
|
#^# ^#
|
||||||
#^ ^#
|
#^ ^#
|
||||||
#^ ^#
|
#^ °^#
|
||||||
#^ #^#
|
#^ #^#
|
||||||
#^ ^#
|
#^ ^#
|
||||||
#^ ^#
|
#^ ^#
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
difficulty = 70
|
||||||
|
min_moves = 16
|
||||||
|
layout = """
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# ° ° #
|
||||||
|
# ## ## #
|
||||||
|
# °### °### #
|
||||||
|
# #### #### #
|
||||||
|
#S ##### ##### E#
|
||||||
|
#####################
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#000000000000000000000000000000KKKKKKKKKKKKKKKKKKXXKKKKKXXXXNWMMNNXXXNNNNNNNNWWWWWMMMMMMMMMMMMMMMMMMM
|
||||||
|
#0000000000000000000000000000000KKKKKKKKKKKKKKKKKXWWXKKKKXXXNNWNXXXXXXNNNNNNNMWWWMMMMMMMMMMMMMMMMMMMM
|
||||||
|
#00000000000000000000000000000000KKKKKKKKKKKKKKKKXMMNKKKKXXXXXXXXXXXXXXNNNNNNNWWWWMMMMMMMMMMMMMMMMMM0
|
||||||
|
#OOO0000000000000000000000000000000KKK00KKKKKKKKKKNWXKKKKXXXXXXXXXNNXXXNNNNNNNWWWWMMMMMMMMNX0OOXMMMOl
|
||||||
|
#OOOO00000000000000000000000000000KKK00000KKKKKKKKKKKKKKKKXXXXXXXXNWNXXXNNNNNNWWWWWMMMMMMNkK0xKMMMNdl
|
||||||
|
#OOOO000000000000000000000000000000KKK00000KKKKKKKKKKKKKKKKXXXXXXNWWNXXXXNNNNNNWWWWMMMMMMNXMNKXWWWOol
|
||||||
|
#OOO00000KKKKKKKXXXXKKK000000000000000000KKKKKKKKKKXXXXXKKXXKKXXNWWWWXXXXXNNNNNWWWWWMMMMMMMOkNWWWWdlc
|
||||||
|
#OO00KXNNNNWWWWWWWWWNKK0000000000000000000KKKKKOdcokooONNNNNXXKXNWWWWNXXXXNXNNNWWWWWWMMMMMMOdXWWWXolc
|
||||||
|
#OO0KKKNWNNNNNXXXXKKK00000000000000000000KKKKKd',,coc,'codOXWXXXNWWWWWXXXXXNXNNNWWWWWWMMMMMXx0NNNXool
|
||||||
|
#OOO00000000000000000000000000000000000KKKK0o,',.',,,;;cl:oookKXNWWWNNXXXXXNNNNNWWWWWWMMMMMMxxXNXKlll
|
||||||
|
#OOOOOO000000000000000000000000000000000KKKO:''',,;:ccloddkKOxokXNWWNK0XKXKx0KXNNWWWWWWWMMMM0d0K00ocl
|
||||||
|
#OOOOOOO00000000000000000000000000000000KK0x;'..';cccccoookO00OOXXXKKKKKKK0dkKXNNWNNWWWWWMMMNdkKK0xc:
|
||||||
|
#OOOOOO00000000000000000000000000000000KKKO,...';cllcclldxxkO0OkXK0000K0KKKOo0KXNNNNNWWWWWMMMkkkdl:cc
|
||||||
|
#0O0OOOO00000000000000000000000000000000KK0:...,:lol::::clooodxOXK000K000K0OoO0KXXNNNNWWWWX0x:,:c::::
|
||||||
|
#0OO0OOOO000000000000000000000000000000000O:,;';llc:,''.';lo.;cOXK000000000OdoOO0KXNNNWWWN;;::;::::::
|
||||||
|
#00OOOOOOOOOOO0000000000000000000000000000Oo';,cllccccc:;;:xddk0XK000000000000KKKKlXNNNNNOc::;;::::::
|
||||||
|
#00OOOOOOOOOOOO0000000000000000000000000000x,,;cllcc:cl:',,cdxk0XKKKKK0KK00000000KKKKOx0Oxd:;;;::;;;;
|
||||||
|
#x00OOOOOOOOOOOOOOOO00000000000000000000000kc..colc:c::;,';cdoxKXXXKK000OOOOOOOO000000dOxxk,,,,;;;;;;
|
||||||
|
#xO00OOOOOOOOOOOOOOOOOOO00000000000000000000, cllc:c;'.;odddddkO0K00000OOOkkkkkkxxddddxxcOc',,;;;;;;
|
||||||
|
#Od000OOOOOOOOOOOOOOOOOO000000000000K0KKKXO'. cllccc:;;:oddooookO000OOkxdoolccccloddxkOK0xk',,;;;,,;
|
||||||
|
#0kx000OOOOOOOO0000000000000000000KKKKKKKKx.. .:c:;:;'.,ccclldxxxkO0kl::ccllodxxkOOO000KXXx,,,,,,,,,
|
||||||
|
#00kkOO000000000OOOOO000000KKK000000000000d'... ..'......cclooddddxkOOOkdddxxkkkkOOO0000KXXk,',,,,,,,
|
||||||
|
#00OocokOkkOOOO000KKK000000000Okkxxxxxxdo:.. ....'......:o;oxdllooddxkOOOkxxkkkkkkkOOOO0KXOlc'''',,,,
|
||||||
|
#000Oxk00KK0000000000000000Oxllodddddol:..'. .;;,,,,'..,:cclxxkkocllodxkkOOkxxxxxxkkkOO00dlOl........
|
||||||
|
#00000000000000000000OOOOOkollloddxxxxd:;;x,',:ccc:::;,,:lolcddxxxdolooxxkOOkkxxxxxxkkOkcclc,:;,,....
|
||||||
|
#000000000000000OOkxxddlccllloooodxxddc....:ooccllllol:,,lddo:loddxxxoloooxO0Okxdddoool:c:c:clldo,,,,
|
||||||
|
#000000OOkxxdolccccllllolllloooddddocc....,dkxl:lxxxxxoc,;lool;:oxkkkxoloodxOK0Oxol:,',:ccllllllllloo
|
||||||
|
#kxdoolcclloodddddxxxxxxoooooddddddc,ccxl:dxxxx::kkkkkko;':lloooccooxkxooooxkOKK0dcc;.:....lllllllloo
|
||||||
|
#loddxxxxkkkxxxxxxxxxxxxloooooddxxo:..'c,lxxxddlldk00Okxc,,codxxlo:l::':cooodkO0KKOo:.,....:lllllllll
|
||||||
|
#kkkkkkkkkkkkkkkkxxxxxxxollolllodoc' .cxoc::locck000Oxc;':dxkxcO:d;;,. ;loodkO0XNN0l.'',,:cccccllll
|
||||||
|
#kkkkkkkkkkkkkxxxxxxxxxxol;dl',:l;.. .,,;...',;dl:dkOOxl;'codd:,..',,;;' ;loodk0KNWWN0c'cccccccccccl
|
||||||
|
#kkkkkkkkkkkkkxxxxxxxxxl:,,;cllooc, ,d...;:;;....,;..;ll:;,,:'........':, :lood0XXNWWWNklccc::cccccc
|
||||||
|
#kkkkkkkkkkkkkxxxxddddd:cclloooddoc'.: .:ldd:'....,,,;;.,;';:;',,;,'.... .coood0NNXNNNXxccc:::::ccc
|
||||||
|
#kkkkkkkkkkkkxxxxddddooclllloodddoc. ..'.,'..',,;clolcc:cooc;,.....'. 'looldOXXXXX0xlc:::::::::
|
||||||
|
#kkkkkkkkkkxxxxdddoooooclllooddddol' .......',:loddddolc;cdddooc;'.. .;. ,clllldxkOOOko::::::::::
|
||||||
|
#kkkkkkkxxxxxddddoooollcllooodxxdol' .';:::clloooddxxddlc:lxxkxdoc;,. ,. .,ccccccllodxx:::::;;;;;
|
||||||
|
#kkkkkkxxxxxdddoooolllllllooodxxxdo; .';:ccccllooddddxxxxoc:lxkxxxxlc:'. . .,;:;:::coo:;;;;;;:cll
|
||||||
|
#kkkkxxxxxdddooolllllllclllooodxxxkxdl,. ';:clllllloodxxxxxxdll;:xkkkxdolc,. .'.',,,,;...:llllc;,,
|
||||||
|
#kkkxxxxddddooollllllllcclllloodddxkkkkkoc;cllllcllodxxxkkOkxdxd:dO000OOxxdo:. .,,,...';:clc;;,,,,,,
|
||||||
|
#kkxxxxdddoooolllllllllc:cclllloooddxxxkxxoccllllloodddxxxkxdoc:cdlloodxxkO00k, ..:cccc:;;,,,,,,,,,,,
|
||||||
|
#kxxxxddooooollllclccccc:,,;:ccllloooooccoddoc;;cllcc:;,,oc''',,lkkd:'..'l,.:ll:cc;,,,,,,,,,,,,,,,,,,
|
||||||
|
#xxxxxddooollllccccccccccc:;.,;;::ccllc:ccccc:lxkcclc, ...;lddxO0Oddd:;;:::;,,,,,,,',,',,,',,,,,,,,
|
||||||
|
#xxxdddooollllccccccccccc::;;...,;:::;.;,,'';c;,ldkkkxxdolodxdxkxxdddoc:,,,,,''''''''''''''''''',,,,,
|
||||||
|
#xxdddoolllllccccccc::::::;;. .;;,,. ..;',clooddddxxddxdolc:,,'''''''''''''''''''''''''''''''''
|
||||||
|
#dddooolllllccccc:::::::;;,. .;:,. ..'.... .;:cllooooddo:,'''''''''''''''''''''''''''''''''''''''
|
||||||
|
#dddoolllllcccccc:::::;;;' .;;..,:looodddol:'..';::cccc'....'''.'''....'....''....''''''...''''''
|
||||||
|
#dooollllccccc::::::;;;;. .. ';,':oodddxxxxxxkkxo;. ................................ ...''''''
|
||||||
|
#ooolllccc:::::::;;;;;;. ';;codddxxxxxxxxkkkkkxl. .................................... ...''''''
|
||||||
|
#ollccccc:::::::;;;;;;, ;cloddxxxxkkxkkkkkkkxoc;;::;;;,.........................................
|
||||||
|
#lllcccc:::::;;;;;;;;;' .coodddxxxxkkkkkkkkxxd';::::::::.........................................
|
||||||
|
#lcccc::;;;;;;;;;;;,,,' clooddxxxxxkkkkkkkkxdd:.;:::::::;........................................
|
||||||
|
#cccc::;;;;;;;;,,,,,,,,. :llodddxxxxkkOOOOkkxddoc. ,::cc:;,...................................
|
||||||
|
#c::::;;;;;;,,,,,,,,'''' ;lloooddxxxxkOOOOOkkxdooc. .:c;.. ............................
|
||||||
|
#:::;;,,,,,,,,,,'''''''. .cloooddxxxxkkOOOOkxxdool; .:cc' ....................... ...
|
||||||
|
#::;;,,,,,,,,,,'''''.. ;ooooddxxxkkOOOOkxxddoolc' . .;cc;................. ......
|
||||||
|
#:;;,',,''''''''''.. ,ooooddxxxxkkOkxxddoolllc:. . ,clc........... . ......,:
|
||||||
|
#;;,'''''''''''''. :oooooooddxxkkkxddoollccc:'. . 'cc, ......;:cl
|
||||||
|
#;,'''''''''''.. 'lolllllooooddxxddoollcccc:,. .. .cc . .. .,;cccl
|
||||||
|
#;'.'''...''.. 'coollllllooooooooollcc:::::,. . .c. . . ..';:ccll
|
||||||
|
#.''........ ,loolllllllooooooollcc::::::;,. . ..';::cccl
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
difficulty = 80
|
||||||
|
min_moves = 28
|
||||||
|
layout = """
|
||||||
|
####################
|
||||||
|
########## E#####
|
||||||
|
######### ##########
|
||||||
|
########## ########
|
||||||
|
########### #
|
||||||
|
############ #### #
|
||||||
|
# ## ###
|
||||||
|
# #### ^^^ ####
|
||||||
|
# ##### ### #####
|
||||||
|
#S###### ######
|
||||||
|
####################
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#000000000000000000000000000000KKKKKKKKKKKKKKKKKKXXKKKKKXXXXNWMMNNXXXNNNNNNNNWWWWWMMMMMMMMMMMMMMMMMMM
|
||||||
|
#0000000000000000000000000000000KKKKKKKKKKKKKKKKKXWWXKKKKXXXNNWNXXXXXXNNNNNNNMWWWMMMMMMMMMMMMMMMMMMMM
|
||||||
|
#00000000000000000000000000000000KKKKKKKKKKKKKKKKXMMNKKKKXXXXXXXXXXXXXXNNNNNNNWWWWMMMMMMMMMMMMMMMMMM0
|
||||||
|
#OOO0000000000000000000000000000000KKK00KKKKKKKKKKNWXKKKKXXXXXXXXXNNXXXNNNNNNNWWWWMMMMMMMMNX0OOXMMMOl
|
||||||
|
#OOOO00000000000000000000000000000KKK00000KKKKKKKKKKKKKKKKXXXXXXXXNWNXXXNNNNNNWWWWWMMMMMMNkK0xKMMMNdl
|
||||||
|
#OOOO000000000000000000000000000000KKK00000KKKKKKKKKKKKKKKKXXXXXXNWWNXXXXNNNNNNWWWWMMMMMMNXMNKXWWWOol
|
||||||
|
#OOO00000KKKKKKKXXXXKKK000000000000000000KKKKKKKKKKXXXXXKKXXKKXXNWWWWXXXXXNNNNNWWWWWMMMMMMMOkNWWWWdlc
|
||||||
|
#OO00KXNNNNWWWWWWWWWNKK0000000000000000000KKKKKOdcokooONNNNNXXKXNWWWWNXXXXNXNNNWWWWWWMMMMMMOdXWWWXolc
|
||||||
|
#OO0KKKNWNNNNNXXXXKKK00000000000000000000KKKKKd',,coc,'codOXWXXXNWWWWWXXXXXNXNNNWWWWWWMMMMMXx0NNNXool
|
||||||
|
#OOO00000000000000000000000000000000000KKKK0o,',.',,,;;cl:oookKXNWWWNNXXXXXNNNNNWWWWWWMMMMMMxxXNXKlll
|
||||||
|
#OOOOOO000000000000000000000000000000000KKKO:''',,;:ccloddkKOxokXNWWNK0XKXKx0KXNNWWWWWWWMMMM0d0K00ocl
|
||||||
|
#OOOOOOO00000000000000000000000000000000KK0x;'..';cccccoookO00OOXXXKKKKKKK0dkKXNNWNNWWWWWMMMNdkKK0xc:
|
||||||
|
#OOOOOO00000000000000000000000000000000KKKO,...';cllcclldxxkO0OkXK0000K0KKKOo0KXNNNNNWWWWWMMMkkkdl:cc
|
||||||
|
#0O0OOOO00000000000000000000000000000000KK0:...,:lol::::clooodxOXK000K000K0OoO0KXXNNNNWWWWX0x:,:c::::
|
||||||
|
#0OO0OOOO000000000000000000000000000000000O:,;';llc:,''.';lo.;cOXK000000000OdoOO0KXNNNWWWN;;::;::::::
|
||||||
|
#00OOOOOOOOOOO0000000000000000000000000000Oo';,cllccccc:;;:xddk0XK000000000000KKKKlXNNNNNOc::;;::::::
|
||||||
|
#00OOOOOOOOOOOO0000000000000000000000000000x,,;cllcc:cl:',,cdxk0XKKKKK0KK00000000KKKKOx0Oxd:;;;::;;;;
|
||||||
|
#x00OOOOOOOOOOOOOOOO00000000000000000000000kc..colc:c::;,';cdoxKXXXKK000OOOOOOOO000000dOxxk,,,,;;;;;;
|
||||||
|
#xO00OOOOOOOOOOOOOOOOOOO00000000000000000000, cllc:c;'.;odddddkO0K00000OOOkkkkkkxxddddxxcOc',,;;;;;;
|
||||||
|
#Od000OOOOOOOOOOOOOOOOOO000000000000K0KKKXO'. cllccc:;;:oddooookO000OOkxdoolccccloddxkOK0xk',,;;;,,;
|
||||||
|
#0kx000OOOOOOOO0000000000000000000KKKKKKKKx.. .:c:;:;'.,ccclldxxxkO0kl::ccllodxxkOOO000KXXx,,,,,,,,,
|
||||||
|
#00kkOO000000000OOOOO000000KKK000000000000d'... ..'......cclooddddxkOOOkdddxxkkkkOOO0000KXXk,',,,,,,,
|
||||||
|
#00OocokOkkOOOO000KKK000000000Okkxxxxxxdo:.. ....'......:o;oxdllooddxkOOOkxxkkkkkkkOOOO0KXOlc'''',,,,
|
||||||
|
#000Oxk00KK0000000000000000Oxllodddddol:..'. .;;,,,,'..,:cclxxkkocllodxkkOOkxxxxxxkkkOO00dlOl........
|
||||||
|
#00000000000000000000OOOOOkollloddxxxxd:;;x,',:ccc:::;,,:lolcddxxxdolooxxkOOkkxxxxxxkkOkcclc,:;,,....
|
||||||
|
#000000000000000OOkxxddlccllloooodxxddc....:ooccllllol:,,lddo:loddxxxoloooxO0Okxdddoool:c:c:clldo,,,,
|
||||||
|
#000000OOkxxdolccccllllolllloooddddocc....,dkxl:lxxxxxoc,;lool;:oxkkkxoloodxOK0Oxol:,',:ccllllllllloo
|
||||||
|
#kxdoolcclloodddddxxxxxxoooooddddddc,ccxl:dxxxx::kkkkkko;':lloooccooxkxooooxkOKK0dcc;.:....lllllllloo
|
||||||
|
#loddxxxxkkkxxxxxxxxxxxxloooooddxxo:..'c,lxxxddlldk00Okxc,,codxxlo:l::':cooodkO0KKOo:.,....:lllllllll
|
||||||
|
#kkkkkkkkkkkkkkkkxxxxxxxollolllodoc' .cxoc::locck000Oxc;':dxkxcO:d;;,. ;loodkO0XNN0l.'',,:cccccllll
|
||||||
|
#kkkkkkkkkkkkkxxxxxxxxxxol;dl',:l;.. .,,;...',;dl:dkOOxl;'codd:,..',,;;' ;loodk0KNWWN0c'cccccccccccl
|
||||||
|
#kkkkkkkkkkkkkxxxxxxxxxl:,,;cllooc, ,d...;:;;....,;..;ll:;,,:'........':, :lood0XXNWWWNklccc::cccccc
|
||||||
|
#kkkkkkkkkkkkkxxxxddddd:cclloooddoc'.: .:ldd:'....,,,;;.,;';:;',,;,'.... .coood0NNXNNNXxccc:::::ccc
|
||||||
|
#kkkkkkkkkkkkxxxxddddooclllloodddoc. ..'.,'..',,;clolcc:cooc;,.....'. 'looldOXXXXX0xlc:::::::::
|
||||||
|
#kkkkkkkkkkxxxxdddoooooclllooddddol' .......',:loddddolc;cdddooc;'.. .;. ,clllldxkOOOko::::::::::
|
||||||
|
#kkkkkkkxxxxxddddoooollcllooodxxdol' .';:::clloooddxxddlc:lxxkxdoc;,. ,. .,ccccccllodxx:::::;;;;;
|
||||||
|
#kkkkkkxxxxxdddoooolllllllooodxxxdo; .';:ccccllooddddxxxxoc:lxkxxxxlc:'. . .,;:;:::coo:;;;;;;:cll
|
||||||
|
#kkkkxxxxxdddooolllllllclllooodxxxkxdl,. ';:clllllloodxxxxxxdll;:xkkkxdolc,. .'.',,,,;...:llllc;,,
|
||||||
|
#kkkxxxxddddooollllllllcclllloodddxkkkkkoc;cllllcllodxxxkkOkxdxd:dO000OOxxdo:. .,,,...';:clc;;,,,,,,
|
||||||
|
#kkxxxxdddoooolllllllllc:cclllloooddxxxkxxoccllllloodddxxxkxdoc:cdlloodxxkO00k, ..:cccc:;;,,,,,,,,,,,
|
||||||
|
#kxxxxddooooollllclccccc:,,;:ccllloooooccoddoc;;cllcc:;,,oc''',,lkkd:'..'l,.:ll:cc;,,,,,,,,,,,,,,,,,,
|
||||||
|
#xxxxxddooollllccccccccccc:;.,;;::ccllc:ccccc:lxkcclc, ...;lddxO0Oddd:;;:::;,,,,,,,',,',,,',,,,,,,,
|
||||||
|
#xxxdddooollllccccccccccc::;;...,;:::;.;,,'';c;,ldkkkxxdolodxdxkxxdddoc:,,,,,''''''''''''''''''',,,,,
|
||||||
|
#xxdddoolllllccccccc::::::;;. .;;,,. ..;',clooddddxxddxdolc:,,'''''''''''''''''''''''''''''''''
|
||||||
|
#dddooolllllccccc:::::::;;,. .;:,. ..'.... .;:cllooooddo:,'''''''''''''''''''''''''''''''''''''''
|
||||||
|
#dddoolllllcccccc:::::;;;' .;;..,:looodddol:'..';::cccc'....'''.'''....'....''....''''''...''''''
|
||||||
|
#dooollllccccc::::::;;;;. .. ';,':oodddxxxxxxkkxo;. ................................ ...''''''
|
||||||
|
#ooolllccc:::::::;;;;;;. ';;codddxxxxxxxxkkkkkxl. .................................... ...''''''
|
||||||
|
#ollccccc:::::::;;;;;;, ;cloddxxxxkkxkkkkkkkxoc;;::;;;,.........................................
|
||||||
|
#lllcccc:::::;;;;;;;;;' .coodddxxxxkkkkkkkkxxd';::::::::.........................................
|
||||||
|
#lcccc::;;;;;;;;;;;,,,' clooddxxxxxkkkkkkkkxdd:.;:::::::;........................................
|
||||||
|
#cccc::;;;;;;;;,,,,,,,,. :llodddxxxxkkOOOOkkxddoc. ,::cc:;,...................................
|
||||||
|
#c::::;;;;;;,,,,,,,,'''' ;lloooddxxxxkOOOOOkkxdooc. .:c;.. ............................
|
||||||
|
#:::;;,,,,,,,,,,'''''''. .cloooddxxxxkkOOOOkxxdool; .:cc' ....................... ...
|
||||||
|
#::;;,,,,,,,,,,'''''.. ;ooooddxxxkkOOOOkxxddoolc' . .;cc;................. ......
|
||||||
|
#:;;,',,''''''''''.. ,ooooddxxxxkkOkxxddoolllc:. . ,clc........... . ......,:
|
||||||
|
#;;,'''''''''''''. :oooooooddxxkkkxddoollccc:'. . 'cc, ......;:cl
|
||||||
|
#;,'''''''''''.. 'lolllllooooddxxddoollcccc:,. .. .cc . .. .,;cccl
|
||||||
|
#;'.'''...''.. 'coollllllooooooooollcc:::::,. . .c. . . ..';:ccll
|
||||||
|
#.''........ ,loolllllllooooooollcc::::::;,. . ..';::cccl
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
difficulty = 60
|
||||||
|
min_moves = 20
|
||||||
|
layout = """
|
||||||
|
# # ## ##° ########### #
|
||||||
|
#S ° ° °# ° ° E#
|
||||||
|
###############################
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#000000000000000000000000000000KKKKKKKKKKKKKKKKKKXXKKKKKXXXXNWMMNNXXXNNNNNNNNWWWWWMMMMMMMMMMMMMMMMMMM
|
||||||
|
#0000000000000000000000000000000KKKKKKKKKKKKKKKKKXWWXKKKKXXXNNWNXXXXXXNNNNNNNMWWWMMMMMMMMMMMMMMMMMMMM
|
||||||
|
#00000000000000000000000000000000KKKKKKKKKKKKKKKKXMMNKKKKXXXXXXXXXXXXXXNNNNNNNWWWWMMMMMMMMMMMMMMMMMM0
|
||||||
|
#OOO0000000000000000000000000000000KKK00KKKKKKKKKKNWXKKKKXXXXXXXXXNNXXXNNNNNNNWWWWMMMMMMMMNX0OOXMMMOl
|
||||||
|
#OOOO00000000000000000000000000000KKK00000KKKKKKKKKKKKKKKKXXXXXXXXNWNXXXNNNNNNWWWWWMMMMMMNkK0xKMMMNdl
|
||||||
|
#OOOO000000000000000000000000000000KKK00000KKKKKKKKKKKKKKKKXXXXXXNWWNXXXXNNNNNNWWWWMMMMMMNXMNKXWWWOol
|
||||||
|
#OOO00000KKKKKKKXXXXKKK000000000000000000KKKKKKKKKKXXXXXKKXXKKXXNWWWWXXXXXNNNNNWWWWWMMMMMMMOkNWWWWdlc
|
||||||
|
#OO00KXNNNNWWWWWWWWWNKK0000000000000000000KKKKKOdcokooONNNNNXXKXNWWWWNXXXXNXNNNWWWWWWMMMMMMOdXWWWXolc
|
||||||
|
#OO0KKKNWNNNNNXXXXKKK00000000000000000000KKKKKd',,coc,'codOXWXXXNWWWWWXXXXXNXNNNWWWWWWMMMMMXx0NNNXool
|
||||||
|
#OOO00000000000000000000000000000000000KKKK0o,',.',,,;;cl:oookKXNWWWNNXXXXXNNNNNWWWWWWMMMMMMxxXNXKlll
|
||||||
|
#OOOOOO000000000000000000000000000000000KKKO:''',,;:ccloddkKOxokXNWWNK0XKXKx0KXNNWWWWWWWMMMM0d0K00ocl
|
||||||
|
#OOOOOOO00000000000000000000000000000000KK0x;'..';cccccoookO00OOXXXKKKKKKK0dkKXNNWNNWWWWWMMMNdkKK0xc:
|
||||||
|
#OOOOOO00000000000000000000000000000000KKKO,...';cllcclldxxkO0OkXK0000K0KKKOo0KXNNNNNWWWWWMMMkkkdl:cc
|
||||||
|
#0O0OOOO00000000000000000000000000000000KK0:...,:lol::::clooodxOXK000K000K0OoO0KXXNNNNWWWWX0x:,:c::::
|
||||||
|
#0OO0OOOO000000000000000000000000000000000O:,;';llc:,''.';lo.;cOXK000000000OdoOO0KXNNNWWWN;;::;::::::
|
||||||
|
#00OOOOOOOOOOO0000000000000000000000000000Oo';,cllccccc:;;:xddk0XK000000000000KKKKlXNNNNNOc::;;::::::
|
||||||
|
#00OOOOOOOOOOOO0000000000000000000000000000x,,;cllcc:cl:',,cdxk0XKKKKK0KK00000000KKKKOx0Oxd:;;;::;;;;
|
||||||
|
#x00OOOOOOOOOOOOOOOO00000000000000000000000kc..colc:c::;,';cdoxKXXXKK000OOOOOOOO000000dOxxk,,,,;;;;;;
|
||||||
|
#xO00OOOOOOOOOOOOOOOOOOO00000000000000000000, cllc:c;'.;odddddkO0K00000OOOkkkkkkxxddddxxcOc',,;;;;;;
|
||||||
|
#Od000OOOOOOOOOOOOOOOOOO000000000000K0KKKXO'. cllccc:;;:oddooookO000OOkxdoolccccloddxkOK0xk',,;;;,,;
|
||||||
|
#0kx000OOOOOOOO0000000000000000000KKKKKKKKx.. .:c:;:;'.,ccclldxxxkO0kl::ccllodxxkOOO000KXXx,,,,,,,,,
|
||||||
|
#00kkOO000000000OOOOO000000KKK000000000000d'... ..'......cclooddddxkOOOkdddxxkkkkOOO0000KXXk,',,,,,,,
|
||||||
|
#00OocokOkkOOOO000KKK000000000Okkxxxxxxdo:.. ....'......:o;oxdllooddxkOOOkxxkkkkkkkOOOO0KXOlc'''',,,,
|
||||||
|
#000Oxk00KK0000000000000000Oxllodddddol:..'. .;;,,,,'..,:cclxxkkocllodxkkOOkxxxxxxkkkOO00dlOl........
|
||||||
|
#00000000000000000000OOOOOkollloddxxxxd:;;x,',:ccc:::;,,:lolcddxxxdolooxxkOOkkxxxxxxkkOkcclc,:;,,....
|
||||||
|
#000000000000000OOkxxddlccllloooodxxddc....:ooccllllol:,,lddo:loddxxxoloooxO0Okxdddoool:c:c:clldo,,,,
|
||||||
|
#000000OOkxxdolccccllllolllloooddddocc....,dkxl:lxxxxxoc,;lool;:oxkkkxoloodxOK0Oxol:,',:ccllllllllloo
|
||||||
|
#kxdoolcclloodddddxxxxxxoooooddddddc,ccxl:dxxxx::kkkkkko;':lloooccooxkxooooxkOKK0dcc;.:....lllllllloo
|
||||||
|
#loddxxxxkkkxxxxxxxxxxxxloooooddxxo:..'c,lxxxddlldk00Okxc,,codxxlo:l::':cooodkO0KKOo:.,....:lllllllll
|
||||||
|
#kkkkkkkkkkkkkkkkxxxxxxxollolllodoc' .cxoc::locck000Oxc;':dxkxcO:d;;,. ;loodkO0XNN0l.'',,:cccccllll
|
||||||
|
#kkkkkkkkkkkkkxxxxxxxxxxol;dl',:l;.. .,,;...',;dl:dkOOxl;'codd:,..',,;;' ;loodk0KNWWN0c'cccccccccccl
|
||||||
|
#kkkkkkkkkkkkkxxxxxxxxxl:,,;cllooc, ,d...;:;;....,;..;ll:;,,:'........':, :lood0XXNWWWNklccc::cccccc
|
||||||
|
#kkkkkkkkkkkkkxxxxddddd:cclloooddoc'.: .:ldd:'....,,,;;.,;';:;',,;,'.... .coood0NNXNNNXxccc:::::ccc
|
||||||
|
#kkkkkkkkkkkkxxxxddddooclllloodddoc. ..'.,'..',,;clolcc:cooc;,.....'. 'looldOXXXXX0xlc:::::::::
|
||||||
|
#kkkkkkkkkkxxxxdddoooooclllooddddol' .......',:loddddolc;cdddooc;'.. .;. ,clllldxkOOOko::::::::::
|
||||||
|
#kkkkkkkxxxxxddddoooollcllooodxxdol' .';:::clloooddxxddlc:lxxkxdoc;,. ,. .,ccccccllodxx:::::;;;;;
|
||||||
|
#kkkkkkxxxxxdddoooolllllllooodxxxdo; .';:ccccllooddddxxxxoc:lxkxxxxlc:'. . .,;:;:::coo:;;;;;;:cll
|
||||||
|
#kkkkxxxxxdddooolllllllclllooodxxxkxdl,. ';:clllllloodxxxxxxdll;:xkkkxdolc,. .'.',,,,;...:llllc;,,
|
||||||
|
#kkkxxxxddddooollllllllcclllloodddxkkkkkoc;cllllcllodxxxkkOkxdxd:dO000OOxxdo:. .,,,...';:clc;;,,,,,,
|
||||||
|
#kkxxxxdddoooolllllllllc:cclllloooddxxxkxxoccllllloodddxxxkxdoc:cdlloodxxkO00k, ..:cccc:;;,,,,,,,,,,,
|
||||||
|
#kxxxxddooooollllclccccc:,,;:ccllloooooccoddoc;;cllcc:;,,oc''',,lkkd:'..'l,.:ll:cc;,,,,,,,,,,,,,,,,,,
|
||||||
|
#xxxxxddooollllccccccccccc:;.,;;::ccllc:ccccc:lxkcclc, ...;lddxO0Oddd:;;:::;,,,,,,,',,',,,',,,,,,,,
|
||||||
|
#xxxdddooollllccccccccccc::;;...,;:::;.;,,'';c;,ldkkkxxdolodxdxkxxdddoc:,,,,,''''''''''''''''''',,,,,
|
||||||
|
#xxdddoolllllccccccc::::::;;. .;;,,. ..;',clooddddxxddxdolc:,,'''''''''''''''''''''''''''''''''
|
||||||
|
#dddooolllllccccc:::::::;;,. .;:,. ..'.... .;:cllooooddo:,'''''''''''''''''''''''''''''''''''''''
|
||||||
|
#dddoolllllcccccc:::::;;;' .;;..,:looodddol:'..';::cccc'....'''.'''....'....''....''''''...''''''
|
||||||
|
#dooollllccccc::::::;;;;. .. ';,':oodddxxxxxxkkxo;. ................................ ...''''''
|
||||||
|
#ooolllccc:::::::;;;;;;. ';;codddxxxxxxxxkkkkkxl. .................................... ...''''''
|
||||||
|
#ollccccc:::::::;;;;;;, ;cloddxxxxkkxkkkkkkkxoc;;::;;;,.........................................
|
||||||
|
#lllcccc:::::;;;;;;;;;' .coodddxxxxkkkkkkkkxxd';::::::::.........................................
|
||||||
|
#lcccc::;;;;;;;;;;;,,,' clooddxxxxxkkkkkkkkxdd:.;:::::::;........................................
|
||||||
|
#cccc::;;;;;;;;,,,,,,,,. :llodddxxxxkkOOOOkkxddoc. ,::cc:;,...................................
|
||||||
|
#c::::;;;;;;,,,,,,,,'''' ;lloooddxxxxkOOOOOkkxdooc. .:c;.. ............................
|
||||||
|
#:::;;,,,,,,,,,,'''''''. .cloooddxxxxkkOOOOkxxdool; .:cc' ....................... ...
|
||||||
|
#::;;,,,,,,,,,,'''''.. ;ooooddxxxkkOOOOkxxddoolc' . .;cc;................. ......
|
||||||
|
#:;;,',,''''''''''.. ,ooooddxxxxkkOkxxddoolllc:. . ,clc........... . ......,:
|
||||||
|
#;;,'''''''''''''. :oooooooddxxkkkxddoollccc:'. . 'cc, ......;:cl
|
||||||
|
#;,'''''''''''.. 'lolllllooooddxxddoollcccc:,. .. .cc . .. .,;cccl
|
||||||
|
#;'.'''...''.. 'coollllllooooooooollcc:::::,. . .c. . . ..';:ccll
|
||||||
|
#.''........ ,loolllllllooooooollcc::::::;,. . ..';::cccl
|
||||||
Reference in New Issue
Block a user