genetic_alg #34

Merged
s464965 merged 18 commits from genetic_alg into master 2022-06-06 15:19:58 +02:00
11 changed files with 110 additions and 46 deletions
Showing only changes of commit e9ef300dde - Show all commits

View File

@ -4,10 +4,11 @@ import heapq
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Tuple, Optional, List from typing import Tuple, Optional, List
from algorithms.genetic.const import MAP_ALIASES
from common.constants import ROWS, COLUMNS, LEFT, RIGHT, UP, DOWN from common.constants import ROWS, COLUMNS, LEFT, RIGHT, UP, DOWN
from common.helpers import directions from common.helpers import directions
EMPTY_FIELDS = ['s', 'g', ' '] EMPTY_FIELDS = [MAP_ALIASES.get("SAND"), MAP_ALIASES.get("GRASS"), ' ']
TURN_LEFT = 'TURN_LEFT' TURN_LEFT = 'TURN_LEFT'
TURN_RIGHT = 'TURN_RIGHT' TURN_RIGHT = 'TURN_RIGHT'

View File

@ -14,12 +14,14 @@ LEFT_KNIGHTS_SPAWN_FIRST_COL = 0
RIGHT_KNIGHTS_SPAWN_FIRST_ROW = 6 RIGHT_KNIGHTS_SPAWN_FIRST_ROW = 6
RIGHT_KNIGHTS_SPAWN_FIRST_COL = 20 RIGHT_KNIGHTS_SPAWN_FIRST_COL = 20
# aliases # map aliases
GRASS = 0 MAP_ALIASES = {
SAND = 1 "GRASS": 0,
WATER = 2 "SAND": 1,
TREE = 3 "WATER": 2,
MONSTER = 4 "TREE": 3,
CASTLE = 5 "MONSTER": 4,
KNIGHT_RED = 6 "CASTLE": 5,
KNIGHT_BLUE = 7 "KNIGHT_RED": 6,
"KNIGHT_BLUE": 7,
}

View File

@ -1,3 +1,4 @@
import string
from random import randrange from random import randrange
from typing import List from typing import List
@ -19,7 +20,7 @@ class Genome:
# todo: spawning castle, monsters, sand, trees in the same way as bellow # todo: spawning castle, monsters, sand, trees in the same way as bellow
self.knights_red = spawn_objects_in_given_area( self.knights_red = spawn_objects_in_given_area(
grid=self.grid, grid=self.grid,
object_alias=KNIGHT_RED, object_alias=MAP_ALIASES.get("KNIGHT_RED"),
objects_count=KNIGHTS_PER_TEAM_COUNT, objects_count=KNIGHTS_PER_TEAM_COUNT,
spawn_position_start=Position(row=LEFT_KNIGHTS_SPAWN_FIRST_ROW, col=LEFT_KNIGHTS_SPAWN_FIRST_COL), spawn_position_start=Position(row=LEFT_KNIGHTS_SPAWN_FIRST_ROW, col=LEFT_KNIGHTS_SPAWN_FIRST_COL),
width=KNIGHTS_SPAWN_WIDTH, width=KNIGHTS_SPAWN_WIDTH,
@ -28,16 +29,30 @@ class Genome:
self.knights_blue = spawn_objects_in_given_area( self.knights_blue = spawn_objects_in_given_area(
grid=self.grid, grid=self.grid,
object_alias=KNIGHT_BLUE, object_alias=MAP_ALIASES.get("KNIGHT_BLUE"),
objects_count=KNIGHTS_PER_TEAM_COUNT, objects_count=KNIGHTS_PER_TEAM_COUNT,
spawn_position_start=Position(row=RIGHT_KNIGHTS_SPAWN_FIRST_ROW, col=RIGHT_KNIGHTS_SPAWN_FIRST_COL), spawn_position_start=Position(row=RIGHT_KNIGHTS_SPAWN_FIRST_ROW, col=RIGHT_KNIGHTS_SPAWN_FIRST_COL),
width=KNIGHTS_SPAWN_WIDTH, width=KNIGHTS_SPAWN_WIDTH,
height=KNIGHTS_SPAWN_HEIGHT height=KNIGHTS_SPAWN_HEIGHT
) )
spawn_objects_in_given_area(
grid=self.grid,
object_alias=MAP_ALIASES.get("CASTLE"),
objects_count=4,
spawn_position_start=Position(row=7, col=9),
width=2,
height=2
)
spawn_where_possible(grid=self.grid, object_alias=MAP_ALIASES.get("WATER"), objects_count=WATER_COUNT)
spawn_where_possible(grid=self.grid, object_alias=MAP_ALIASES.get("TREE"), objects_count=TREE_COUNT)
spawn_where_possible(grid=self.grid, object_alias=MAP_ALIASES.get("SAND"), objects_count=SAND_COUNT)
spawn_where_possible(grid=self.grid, object_alias=MAP_ALIASES.get("MONSTER"), objects_count=MONSTERS_COUNT)
def is_empty(grid: npt.NDArray, position: Position) -> bool: def is_empty(grid: npt.NDArray, position: Position) -> bool:
return grid[position.row, position.col] in [GRASS, SAND] return grid[position.row, position.col] in [MAP_ALIASES.get("GRASS"), MAP_ALIASES.get("SAND")]
def is_invalid_area(spawn_position_start, height, width) -> bool: def is_invalid_area(spawn_position_start, height, width) -> bool:
@ -47,7 +62,8 @@ def is_invalid_area(spawn_position_start, height, width) -> bool:
spawn_position_start.col + width - 1 >= COLUMNS spawn_position_start.col + width - 1 >= COLUMNS
def spawn_objects_in_given_area(grid: npt.NDArray, object_alias: KNIGHT_RED | KNIGHT_BLUE | CASTLE | MONSTER, def spawn_objects_in_given_area(grid: npt.NDArray,
object_alias: MAP_ALIASES.get("KNIGHT_RED") | MAP_ALIASES.get("KNIGHT_BLUE") | MAP_ALIASES.get("CASTLE") | MAP_ALIASES.get("MONSTER"),
objects_count: int = 1, objects_count: int = 1,
spawn_position_start: Position = Position(row=0, col=0), spawn_position_start: Position = Position(row=0, col=0),
width: int = 1, width: int = 1,
@ -69,3 +85,16 @@ def spawn_objects_in_given_area(grid: npt.NDArray, object_alias: KNIGHT_RED | KN
objects_remaining -= 1 objects_remaining -= 1
return positions return positions
# temporary function to spawn castle, monsters, sand, trees
def spawn_where_possible(grid: npt.NDArray, object_alias: string, objects_count: int = 1):
objects_remaining = int(objects_count)
while objects_remaining > 0:
row = randrange(0, ROWS - 1)
col = randrange(0, COLUMNS - 1)
position = Position(row=row, col=col)
if is_empty(grid, position):
grid[position.row, position.col] = object_alias
objects_remaining -= 1

View File

@ -1,3 +1,4 @@
from map_importer_exporter import export_map
from genome import Genome from genome import Genome
@ -6,6 +7,7 @@ def main() -> None:
print(example_genome.knights_red) print(example_genome.knights_red)
print(example_genome.knights_blue) print(example_genome.knights_blue)
print(example_genome.grid) print(example_genome.grid)
export_map(example_genome.grid)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -0,0 +1,40 @@
import json
import random
import string
from datetime import datetime
import numpy
import numpy.typing as npt
from os import listdir
from os.path import isfile, join
# Save map to file
def export_map(grid: npt.NDArray):
json_data = {"map": grid.tolist()}
now = datetime.now()
file_name = "map_" + now.strftime("%Y_%m_%d_%H_%M_%S") + ".json"
with open("resources/maps/" + file_name, "w") as write_file:
json.dump(json_data, write_file)
print("Saved map to file " + file_name)
def import_random_map() -> object:
path = "resources/maps"
files = [f for f in listdir(path) if isfile(join(path, f))]
random_map_name = random.choice(files)
return import_map(random_map_name)
# Read map from file
def import_map(file: string) -> object:
with open("resources/maps/" + file, "r") as read_file:
print("Reading map from file " + file)
decoded_json = json.load(read_file)
decoded_grid = numpy.asarray(decoded_json["map"])
print(decoded_grid)
return decoded_grid.tolist()

View File

@ -6,7 +6,7 @@ GAME_TITLE = 'WMICraft'
WINDOW_HEIGHT = 800 WINDOW_HEIGHT = 800
WINDOW_WIDTH = 1360 WINDOW_WIDTH = 1360
FPS_COUNT = 60 FPS_COUNT = 60
TURN_INTERVAL = 300 TURN_INTERVAL = 500
GRID_CELL_PADDING = 5 GRID_CELL_PADDING = 5
GRID_CELL_SIZE = 36 GRID_CELL_SIZE = 36

View File

@ -1,6 +1,8 @@
from typing import Tuple, List from typing import Tuple, List
import pygame import pygame
from algorithms.genetic.const import MAP_ALIASES
from common.constants import GRID_CELL_PADDING, GRID_CELL_SIZE, COLUMNS, ROWS, CLASSES, CLASS_TO_ID from common.constants import GRID_CELL_PADDING, GRID_CELL_SIZE, COLUMNS, ROWS, CLASSES, CLASS_TO_ID
import csv import csv
import os import os
@ -99,7 +101,7 @@ def castle_neighbors(map, castle_bottom_right_row, castle_bottom_right_col):
return neighbors return neighbors
def find_neighbours(grid: List[List[str]], col: int, row: int) -> List[Tuple[int, int]]: def find_neighbours(grid: List[List[int]], col: int, row: int) -> List[Tuple[int, int]]:
dr = [-1, 1, 0, 0] dr = [-1, 1, 0, 0]
dc = [0, 0, -1, 1] dc = [0, 0, -1, 1]
@ -111,7 +113,7 @@ def find_neighbours(grid: List[List[str]], col: int, row: int) -> List[Tuple[int
if rr < 0 or cc < 0: continue if rr < 0 or cc < 0: continue
if rr >= ROWS or cc >= COLUMNS: continue if rr >= ROWS or cc >= COLUMNS: continue
if grid[rr][cc] not in ['g', 's', '.']: continue if grid[rr][cc] not in [MAP_ALIASES.get("GRASS"), MAP_ALIASES.get("SAND"), '.']: continue
neighbours.append((rr, cc)) neighbours.append((rr, cc))
return neighbours return neighbours

View File

@ -3,10 +3,11 @@ import random
import pygame import pygame
from algorithms.a_star import a_star, State, TURN_RIGHT, TURN_LEFT, FORWARD from algorithms.a_star import a_star, State, TURN_RIGHT, TURN_LEFT, FORWARD
from algorithms.genetic.const import MAP_ALIASES
from algorithms.genetic.map_importer_exporter import import_random_map
from common.constants import * from common.constants import *
from learning.decision_tree import DecisionTree from learning.decision_tree import DecisionTree
from logic.knights_queue import KnightsQueue from logic.knights_queue import KnightsQueue
from logic.spawner import Spawner
from models.castle import Castle from models.castle import Castle
from models.knight import Knight from models.knight import Knight
from models.monster import Monster from models.monster import Monster
@ -21,7 +22,7 @@ class Level:
# sprite group setup # sprite group setup
self.sprites = pygame.sprite.LayeredUpdates() self.sprites = pygame.sprite.LayeredUpdates()
self.map = [['g' for _ in range(COLUMNS)] for y in range(ROWS)] self.map = []
self.list_knights_blue = [] self.list_knights_blue = []
self.list_knights_red = [] self.list_knights_red = []
@ -31,27 +32,11 @@ class Level:
self.knights_queue = None self.knights_queue = None
def create_map(self): def create_map(self):
self.generate_map() self.map = import_random_map()
self.setup_base_tiles() self.setup_base_tiles()
self.setup_objects() self.setup_objects()
self.knights_queue = KnightsQueue(self.list_knights_blue, self.list_knights_red) self.knights_queue = KnightsQueue(self.list_knights_blue, self.list_knights_red)
def generate_map(self):
spawner = Spawner(self.map)
spawner.spawn_where_possible(['w' for _ in range(NBR_OF_WATER)])
spawner.spawn_where_possible(['t' for _ in range(NBR_OF_TREES)])
spawner.spawn_where_possible(['s' for _ in range(NBR_OF_SANDS)])
spawner.spawn_in_area(['k_b' for _ in range(4)], LEFT_KNIGHTS_SPAWN_FIRST_ROW, LEFT_KNIGHTS_SPAWN_FIRST_COL,
KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT)
spawner.spawn_in_area(['k_r' for _ in range(4)], RIGHT_KNIGHTS_SPAWN_FIRST_ROW, RIGHT_KNIGHTS_SPAWN_FIRST_COL,
KNIGHTS_SPAWN_WIDTH, KNIGHTS_SPAWN_HEIGHT)
spawner.spawn_in_area(['c'], CASTLE_SPAWN_FIRST_ROW, CASTLE_SPAWN_FIRST_COL, CASTLE_SPAWN_WIDTH,
CASTLE_SPAWN_HEIGHT, 2)
spawner.spawn_where_possible(['m' for _ in range(NBR_OF_MONSTERS)])
def setup_base_tiles(self): def setup_base_tiles(self):
textures = [] textures = []
for texture_path in TILES: for texture_path in TILES:
@ -63,15 +48,15 @@ class Level:
for col_index, col in enumerate(row): for col_index, col in enumerate(row):
# add base tiles, e.g. water, tree, grass # add base tiles, e.g. water, tree, grass
if col == "w": if col == MAP_ALIASES.get('WATER'):
texture_index = 5 texture_index = 5
texture_surface = textures[texture_index][1] texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites, 'w') Tile((col_index, row_index), texture_surface, self.sprites, 'w')
elif col == "t": elif col == MAP_ALIASES.get('TREE'):
texture_index = 6 texture_index = 6
texture_surface = textures[texture_index][1] texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites, 't') Tile((col_index, row_index), texture_surface, self.sprites, 't')
elif col == "s": elif col == MAP_ALIASES.get('SAND'):
texture_index = 4 texture_index = 4
texture_surface = textures[texture_index][1] texture_surface = textures[texture_index][1]
Tile((col_index, row_index), texture_surface, self.sprites) Tile((col_index, row_index), texture_surface, self.sprites)
@ -88,19 +73,19 @@ class Level:
for col_index, col in enumerate(row): for col_index, col in enumerate(row):
# add objects, e.g. knights, monsters, castle # add objects, e.g. knights, monsters, castle
if col == "k_b": if col == MAP_ALIASES.get('KNIGHT_BLUE'):
knight = Knight(self.screen, (col_index, row_index), self.sprites, "blue") knight = Knight(self.screen, (col_index, row_index), self.sprites, "blue")
self.map[row_index][col_index] = knight self.map[row_index][col_index] = knight
self.list_knights_blue.append(knight) self.list_knights_blue.append(knight)
elif col == "k_r": elif col == MAP_ALIASES.get('KNIGHT_RED'):
knight = Knight(self.screen, (col_index, row_index), self.sprites, "red") knight = Knight(self.screen, (col_index, row_index), self.sprites, "red")
self.map[row_index][col_index] = knight self.map[row_index][col_index] = knight
self.list_knights_red.append(knight) self.list_knights_red.append(knight)
elif col == "m": elif col == MAP_ALIASES.get('MONSTER'):
monster = Monster(self.screen, (col_index, row_index), self.sprites) monster = Monster(self.screen, (col_index, row_index), self.sprites)
self.map[row_index][col_index] = monster self.map[row_index][col_index] = monster
self.list_monsters.append(monster) self.list_monsters.append(monster)
elif col == "c": elif col == MAP_ALIASES.get('CASTLE'):
castle_count += 1 castle_count += 1
if castle_count == 4: if castle_count == 4:
castle = Castle(self.screen, (col_index, row_index), self.sprites) castle = Castle(self.screen, (col_index, row_index), self.sprites)
@ -108,15 +93,15 @@ class Level:
self.list_castles.append(castle) self.list_castles.append(castle)
def handle_turn(self): def handle_turn(self):
print("next turn")
current_knight = self.knights_queue.dequeue_knight() current_knight = self.knights_queue.dequeue_knight()
print("next turn " + current_knight.team)
knight_pos_x = current_knight.position[0] knight_pos_x = current_knight.position[0]
knight_pos_y = current_knight.position[1] knight_pos_y = current_knight.position[1]
goal_list = self.decision_tree.predict_move(grid=self.map, current_knight=current_knight, goal_list = self.decision_tree.predict_move(grid=self.map, current_knight=current_knight,
monsters=self.list_monsters, monsters=self.list_monsters,
opponents=self.list_knights_red opponents=self.list_knights_red
if current_knight.team_alias == 'k_r' else self.list_knights_blue, if current_knight.team_alias() == 'k_r' else self.list_knights_blue,
castle=self.list_castles[0]) castle=self.list_castles[0])
if len(goal_list) == 0: if len(goal_list) == 0:
@ -139,7 +124,7 @@ class Level:
current_knight.rotate_right() current_knight.rotate_right()
elif next_action == FORWARD: elif next_action == FORWARD:
current_knight.step_forward() current_knight.step_forward()
self.map[knight_pos_y][knight_pos_x] = 'g' self.map[knight_pos_y][knight_pos_x] = MAP_ALIASES.get("GRASS")
# update knight on map # update knight on map
if current_knight.direction.name == UP: if current_knight.direction.name == UP:

View File

@ -0,0 +1 @@
{"map": [[0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 4, 2, 3, 0, 0, 3, 3, 0, 3, 4, 0, 0], [0, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0, 0], [0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [3, 1, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0], [3, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 6, 0, 0, 0, 0, 3, 5, 5, 0, 0, 0, 0, 1, 0, 1, 3, 0, 0, 7, 0, 0], [2, 0, 3, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0], [6, 6, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 6, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 3, 0, 2, 2, 0, 2, 0, 3, 0, 0, 7, 0], [0, 0, 3, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0], [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0], [1, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0], [0, 2, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}

View File

@ -0,0 +1 @@
{"map": [[0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0, 3, 0, 0, 3, 0, 0], [0, 0, 0, 1, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 2, 1, 2, 3, 0, 3, 0, 0, 0, 0], [3, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0], [0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, 0, 0], [0, 6, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 3, 0, 0, 7, 7], [0, 0, 3, 2, 0, 0, 0, 0, 0, 5, 5, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 6, 0, 0, 0, 0, 1, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0], [0, 0, 6, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 2, 2, 0, 0, 0, 3, 0, 7, 0], [0, 6, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 1, 0, 0, 0], [0, 3, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 7, 0, 0], [0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 3, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0], [0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 3, 3, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 3, 0, 0, 0, 3, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}

View File

@ -0,0 +1 @@
{"map": [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0], [0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0], [2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [1, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 2, 0], [0, 1, 0, 0, 3, 0, 0, 1, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0], [6, 0, 0, 0, 1, 2, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 3, 0, 0, 0, 5, 5, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 2, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 7, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 0, 0], [6, 2, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 7, 0, 7], [0, 3, 6, 2, 0, 3, 0, 0, 0, 1, 0, 0, 3, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 3, 3, 0, 0, 0, 4, 0, 0, 0, 3, 1, 0, 0, 0], [0, 3, 2, 3, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0], [0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 3, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}