From 306b6f4e9a12d4c036e2ed23f505bd1553800609 Mon Sep 17 00:00:00 2001 From: anton Date: Sun, 12 May 2024 15:16:19 +0200 Subject: [PATCH] Upload files to "/" --- ShipDirection.py | 15 +++ ShipField.py | 255 +++++++++++++++++++++++++++++++++++++++++++++++ ShipMode.py | 15 +++ ShootResult.py | 8 ++ main.py | 171 +++++++++++++++++++++++++++++++ 5 files changed, 464 insertions(+) create mode 100644 ShipDirection.py create mode 100644 ShipField.py create mode 100644 ShipMode.py create mode 100644 ShootResult.py create mode 100644 main.py diff --git a/ShipDirection.py b/ShipDirection.py new file mode 100644 index 0000000..f7aa4d3 --- /dev/null +++ b/ShipDirection.py @@ -0,0 +1,15 @@ +from enum import Enum + + +class ShipDirection(Enum): + VERTICAL = "VERTICAL" + HORIZONTAL = "HORIZONTAL" + UNKNOWN = "UNKNOWN" + + @staticmethod + def from_string(raw_value): + if raw_value: + value = raw_value.upper() + if value in ShipDirection.__members__: + return ShipDirection[value] + return ShipDirection.UNKNOWN diff --git a/ShipField.py b/ShipField.py new file mode 100644 index 0000000..99b0d66 --- /dev/null +++ b/ShipField.py @@ -0,0 +1,255 @@ +import copy +from ShootResult import ShootResult +from ShipMode import ShipMode +from ShipDirection import ShipDirection + + +class ShipField: + + def __init__(self): + self.field = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] + + self.ships = [4, 3, 3, 2, 2, 2, 1, 1, 1, 1] + self.field_size = 10 + self.field_mode = ShipMode.PUT + self.ship_size = 4 + self.ship_direction = ShipDirection.VERTICAL + + def from_json(self, obj): + self.field = obj['field'] + self.ships = obj['ships'] + self.field_size = obj['field_size'] + self.field_mode = ShipMode.from_string(obj['field_mode']) + self.ship_size = obj['ship_size'] + self.ship_direction = ShipDirection.from_string(obj['ship_direction']) + + def __getitem__(self, item): + if item is None: + return None + + if type(item) is not int and item.isnumeric(): + item = int(item) + + if type(item) is int and 0 <= item < len(self.field): + return self.field[item] + + return None + + def action(self, row, col): + self.clear_marker() + + if self.field_mode == ShipMode.PUT: + if self.check_ship(row, col): + self.get_ship(row, col) + + elif self.ship_size in self.ships and self.check_possible(row, col): + self.set_ship(row, col) + + elif self.field_mode == ShipMode.SHOOT: + self.shoot(row, col) + + def target(self, row, col): + self.clear_marker() + + if self.field_mode == ShipMode.PUT: + if self.check_possible(row, col): + if self.ship_direction == ShipDirection.VERTICAL: + for r in range(row, row + self.ship_size): + if self.ship_size in self.ships: + self.field[r * self.field_size + col] = "p" + else: + self.field[r * self.field_size + col] = "r" + + if self.ship_direction == ShipDirection.HORIZONTAL: + for c in range(col, col + self.ship_size): + if self.ship_size in self.ships: + self.field[row * self.field_size + c] = "p" + else: + self.field[row * self.field_size + c] = "r" + else: + self.field[row * self.field_size + col] += "+" + + def clear_marker(self): + for i in range(0, len(self.field)): + if self.field[i] == "p" or self.field[i] == "r": + self.field[i] = "" + + if "+" in self.field[i]: + self.field[i] = self.field[i].replace("+", "") + + def set_ship(self, row, col): + if row < 0 or row > self.field_size: + return + if col < 0 or col > self.field_size: + return + index = row * self.field_size + col + if self.ship_direction == ShipDirection.VERTICAL: + if self.field_size - row < self.ship_size: + return + for r in range(row, row + self.ship_size): + index = r * self.field_size + col + self.field[index] = "1" + if self.ship_direction == ShipDirection.HORIZONTAL: + if self.field_size - col < self.ship_size: + return + for c in range(col, col + self.ship_size): + index = row * self.field_size + c + self.field[index] = "1" + + if self.ship_size in self.ships: + self.ships.remove(self.ship_size) + + def get_ship(self, row, col): + if row < 0 or row > self.field_size: + return + if col < 0 or col > self.field_size: + return + + self.field[row * self.field_size + col] = '' + + ship_size = 1 + ship_direction = ShipDirection.UNKNOWN + + # check vertical + for r in range(row + 1, self.field_size): + if self.check_ship(r, col): + ship_size += 1 + ship_direction = ShipDirection.VERTICAL + self.field[r * self.field_size + col] = '' + else: + break + + for r in range(row - 1, -1, -1): + if self.check_ship(r, col): + ship_size += 1 + ship_direction = ShipDirection.VERTICAL + self.field[r * self.field_size + col] = '' + else: + break + + if ship_direction == ShipDirection.UNKNOWN: + # check horizontal + for c in range(col + 1, self.field_size): + if self.check_ship(row, c): + ship_size += 1 + ship_direction = ShipDirection.HORIZONTAL + self.field[row * self.field_size + c] = '' + else: + break + + for c in range(col - 1, - 1, -1): + if self.check_ship(row, c): + ship_size += 1 + ship_direction = ShipDirection.HORIZONTAL + self.field[row * self.field_size + c] = '' + else: + break + + self.set_ship_direction(ship_direction) + self.set_ship_size(self.ship_size) + + self.ships.append(self.ship_size) + + def shoot(self, row, col): + if row < 0 or row > self.field_size - 1: + return ShootResult.UNDEFINED + if col < 0 or col > self.field_size - 1: + return ShootResult.UNDEFINED + index = row * self.field_size + col + if (self.field[index]).strip() == "": + self.field[index] = "0" + return ShootResult.EMPTY + elif (self.field[index]).strip() == "1": + self.field[index] = "\\" + return ShootResult.DAMAGED + else: + return ShootResult.UNDEFINED + + def check_ship(self, row, col): + return self.field[row * self.field_size + col].strip() == '1' + + def check_possible(self, row, col): + if self.ship_direction == ShipDirection.VERTICAL: + if self.field_size - row >= self.ship_size: + for r in range(row, row + self.ship_size): + if not self.check_blocked(r, col): + return False + return True + + if self.ship_direction == ShipDirection.HORIZONTAL: + if self.field_size - col >= self.ship_size: + for c in range(col, col + self.ship_size): + if not self.check_blocked(row, c): + return False + return True + + return False + + def check_blocked(self, row, col): + for r in range(row - 1, row + 2): + for c in range(col - 1, col + 2): + if 0 <= r < self.field_size and 0 <= c < self.field_size: + cell = (self.field[r * self.field_size + c]).strip() + if cell != '' and cell != 'p': + return False + return True + + def set_ship_size(self, value): + if value is None: + return + + if type(value) is str and value.isnumeric(): + value = int(value) + + if type(value) is int and 1 <= value <= 4: + self.ship_size = value + + def set_ship_direction(self, value): + if value is None: + return + + if type(value) is not ShipDirection: + return + + if value != ShipDirection.UNKNOWN: + self.ship_direction = value + + def toggle_ship_direction(self): + if self.field_mode == ShipMode.PUT: + if self.ship_direction == ShipDirection.VERTICAL: + self.ship_direction = ShipDirection.HORIZONTAL + else: + self.ship_direction = ShipDirection.VERTICAL + + def toggle_field_mode(self): + if self.field_mode == ShipMode.PUT: + self.field_mode = ShipMode.SHOOT + else: + self.field_mode = ShipMode.PUT + + def print_field(self): + for r in range(0, self.field_size): + blocked_string = "" + ship_string = "" + for c in range(0, self.field_size): + blocked_string += str(self.check_blocked(r, c))[0] + ", " + ship_string += self.field[r * self.field_size + c] + ', ' + print(blocked_string[:-2] + ' ' + ship_string[:-2]) + print("********************************************************************") + + @staticmethod + def convert_to_json(obj): + if isinstance(obj, ShipField): + result = copy.copy(obj.__dict__) + result['field_mode'] = obj.field_mode.value + result['ship_direction'] = obj.ship_direction.value + return result \ No newline at end of file diff --git a/ShipMode.py b/ShipMode.py new file mode 100644 index 0000000..91eea6c --- /dev/null +++ b/ShipMode.py @@ -0,0 +1,15 @@ +from enum import Enum + + +class ShipMode(Enum): + PUT = "PUT" + SHOOT = "SHOOT" + UNKNOWN = "UNKNOWN" + + @staticmethod + def from_string(raw_value): + if raw_value: + value = raw_value.upper() + if value in ShipMode.__members__: + return ShipMode[value] + return ShipMode.UNKNOWN diff --git a/ShootResult.py b/ShootResult.py new file mode 100644 index 0000000..b64107a --- /dev/null +++ b/ShootResult.py @@ -0,0 +1,8 @@ +from enum import Enum + + +class ShootResult(Enum): + EMPTY = "EMPTY" + DAMAGED = "DAMAGED" + KILLED = "KILLED" + UNDEFINED = "UNDEFINED" diff --git a/main.py b/main.py new file mode 100644 index 0000000..30446f6 --- /dev/null +++ b/main.py @@ -0,0 +1,171 @@ +import json +import os +import time +from tkinter import filedialog +from tkinter import * +from ShipField import ShipField + +my_field = ShipField() +enemy_field = ShipField() + +active_field = my_field +active_text = {} + + +def draw_field(window, field, col_offset=0, row_offset=0): + buttons = [] + for r in range(0, field.field_size): + for c in range(0, field.field_size): + btn = Button(window, text='', width=5, height=2) + btn.grid(column=c + col_offset, row=r + row_offset) + btn.bind('', lambda e, x=r, y=c: left_button_click(buttons, x, y)) + btn.bind('', right_button_click) + btn.bind('', lambda e, x=r, y=c: button_enter(buttons, x, y)) + buttons.append(btn) + colorize(field, buttons) + return buttons + + +def colorize(field, buttons): + for i in range(len(field.field)): + bg = "white" + if field.field[i] == "1": + bg = 'pink' + if field.field[i] == "\\": + bg = 'grey' + if field.field[i] == "0": + bg = 'black' + if field.field[i] == "p": + bg = 'blue' + if field.field[i] == "r": + bg = 'red' + if "+" in field.field[i]: + bg = 'orange' + buttons[i].configure(bg=bg) + + +def keypress_handler(e): + global active_field + if e.keysym.isnumeric(): + active_field.set_ship_size(int(e.keysym)) + else: + if e.keysym == 'm': + active_field.toggle_field_mode() + + +def left_button_click(buttons, row, col): + global active_field + global active_text + + active_field.action(row, col) + colorize(active_field, buttons) + + refresh_remaining_ships_label(active_field, active_text) + + +def right_button_click(d): + global active_field + active_field.toggle_ship_direction() + + +def button_enter(buttons, row, col): + global active_field + global active_text + + if buttons == my_buttons: + active_field = my_field + active_text = my_remainingShipsText + enemy_field.clear_marker() + my_field.target(row, col) + + elif buttons == enemy_buttons: + active_field = enemy_field + active_text = enemy_remainingShipsText + my_field.clear_marker() + enemy_field.target(row, col) + + colorize(my_field, my_buttons) + colorize(enemy_field, enemy_buttons) + + +def savebutton_click(field): + file_path = filedialog.asksaveasfilename(filetypes=[("JSON files", "*.json")]) + + if file_path: + with open(file_path, 'w') as f: + json.dump({'shipField': field}, f, default=ShipField.convert_to_json) + + +def loadbutton_click(field, buttons): + file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")]) + + if os.path.isfile(file_path): + with open(file_path) as lines: + field.from_json(json.load(lines)["shipField"]) + + colorize(field, buttons) + + +def refresh_remaining_ships_label(field, stringvar): + text = '' + for i in range(1, 5): + count = field.ships.count(i) + if count > 0: + text += f'{"[]" * i}: {count}, ' + + stringvar.set(text[:-2]) + + +window = Tk() +window.title("Ship Craft!") +window.geometry('1020x540') +window.bind_all('', keypress_handler) + +my_remainingShipsText = StringVar() +enemy_remainingShipsText = StringVar() + +start_column_my_field = 1 +start_row_my_field = 1 + +start_column_enemy_field = start_column_my_field + my_field.field_size + 1 +start_row_enemy_field = start_row_my_field + +col_vertical_separator = start_column_my_field + my_field.field_size +row_horizontal_separator = start_row_my_field + my_field.field_size + +load_button_row = start_row_my_field + my_field.field_size + 1 + +my_buttons = draw_field(window, my_field, start_column_my_field, start_row_my_field) +enemy_buttons = draw_field(window, enemy_field, start_column_enemy_field, start_row_enemy_field) + +if start_column_my_field > 0: + lbl_left_vertical = Label(window, text='', width=5, height=2) + lbl_left_vertical.grid(column=start_column_my_field - 1, row=start_row_my_field) + +lbl_center_vertical = Label(window, text='', width=5, height=2) +lbl_center_vertical.grid(column=col_vertical_separator, row=start_row_my_field) + +if start_row_my_field > 0: + lbl_upper_horizontal = Label(window, text='', width=5, height=2) + lbl_upper_horizontal.grid(column=start_column_my_field, row=start_row_my_field - 1) + +lbl_lower_horizontal = Label(window, text='', width=50, height=2, textvariable=my_remainingShipsText) +lbl_lower_horizontal.grid(column=start_column_my_field, row=row_horizontal_separator, columnspan=10) + +lbl_lower_enemy_horizontal = Label(window, text='', width=50, height=2, textvariable=enemy_remainingShipsText) +lbl_lower_enemy_horizontal.grid(column=start_column_enemy_field, row=row_horizontal_separator, columnspan=10) + +savebutton = Button(window, text='Save', width=20, height=2, command=lambda: savebutton_click(my_field)) +savebutton.grid(column=start_column_my_field, row=load_button_row, columnspan=4) + +loadbutton = Button(window, text='Load', width=20, height=2, command=lambda: loadbutton_click(my_field, my_buttons)) +loadbutton.grid(column=start_column_my_field + 6, row=load_button_row, columnspan=4) + +savebutton_enemy = Button(window, text='Save', width=20, height=2, command=lambda: savebutton_click(enemy_field)) +savebutton_enemy.grid(column=start_column_enemy_field, row=load_button_row, columnspan=4) + +loadbutton_enemy = Button(window, text='Load', width=20, height=2, command=lambda: loadbutton_click(enemy_field, enemy_buttons)) +loadbutton_enemy.grid(column=start_column_enemy_field + 6, row=load_button_row, columnspan=4) + + +window.mainloop()