diff --git a/HNS/MB/ShipDirection.py b/HNS/MB/ShipDirection.py new file mode 100644 index 0000000..522abd6 --- /dev/null +++ b/HNS/MB/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/HNS/MB/ShipField.py b/HNS/MB/ShipField.py index f179f24..fe38246 100644 --- a/HNS/MB/ShipField.py +++ b/HNS/MB/ShipField.py @@ -1,4 +1,8 @@ +import copy + from ShootResult import ShootResult +from ShipDirection import ShipDirection +from ShipMode import ShipMode class ShipField: @@ -15,10 +19,19 @@ class ShipField: ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] + self.ships = [4, 3, 3, 2, 2, 2, 1, 1, 1, 1] self.field_size = 10 - self.field_mode = 0 + self.field_mode = ShipMode.PUT self.ship_size = 4 - self.ship_direction = 0 + 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: @@ -35,31 +48,40 @@ class ShipField: def action(self, row, col): self.clear_marker() - if self.field_mode == 0: - if self.check_possible(row, col): + 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 == 1: + elif self.field_mode == ShipMode.SHOOT: self.shoot(row, col) def target(self, row, col): self.clear_marker() - if self.field_mode == 0: + if self.field_mode == ShipMode.PUT: if self.check_possible(row, col): - if self.ship_direction == 0: + if self.ship_direction == ShipDirection.VERTICAL: for r in range(row, row + self.ship_size): - self.field[r * self.field_size + col] = "p" + 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 == 1: + if self.ship_direction == ShipDirection.HORIZONTAL: for c in range(col, col + self.ship_size): - self.field[row * self.field_size + c] = "p" + 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": + if self.field[i] == "p" or self.field[i] == "r": self.field[i] = "" if "+" in self.field[i]: @@ -71,19 +93,73 @@ class ShipField: if col < 0 or col > self.field_size: return index = row * self.field_size + col - if self.ship_direction == 0: + 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 == 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 + + # Проверим вертикаль + 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: + # Проверим горизонталь + 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(ship_size) + + self.ships.append(ship_size) + def shoot(self, row, col): if row < 0 or row > self.field_size - 1: return ShootResult.UNDEFINED @@ -99,10 +175,15 @@ class ShipField: else: return ShootResult.UNDEFINED + def check_ship(self, row, col): + # Функция должна возвращать True, если в заданной клетке есть корабль, + # в противном случае - False + return self.field[row * self.field_size + col].strip() == '1' + def check_possible(self, row, col): # Функция должна возвращать True, если можно поставить сюда корабль, # в противном случае - False - if self.ship_direction == 0: + if self.ship_direction == ShipDirection.VERTICAL: # Здесь мы знаем, что корабль помещается на поле. if self.field_size - row >= self.ship_size: # Теперь нужно проверить, не заблокировано ли какое-то из полей, @@ -111,7 +192,7 @@ class ShipField: return False return True - if self.ship_direction == 1: + 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): @@ -145,25 +226,26 @@ class ShipField: if value is None: return - if type(value) is str and value.isnumeric(): - value = int(value) + if type(value) is not ShipDirection: + return - if type(value) is int and 0 <= value <= 1: + if value != ShipDirection.UNKNOWN: self.ship_direction = value def toggle_ship_direction(self): - if self.ship_direction == 0: - self.ship_direction = 1 + if self.ship_direction == ShipDirection.VERTICAL: + self.ship_direction = ShipDirection.HORIZONTAL else: - self.ship_direction = 0 + self.ship_direction = ShipDirection.VERTICAL def toggle_field_mode(self): - if self.field_mode == 0: - self.field_mode = 1 + if self.field_mode == ShipMode.PUT: + self.field_mode = ShipMode.SHOOT else: - self.field_mode = 0 + self.field_mode = ShipMode.PUT def print_field(self): + print(self.ships) for r in range(0, self.field_size): blocked_string = "" ship_string = "" @@ -171,5 +253,14 @@ class ShipField: blocked_string += str(self.check_blocked(r, c))[0] + ", " ship_string += self.field[r * self.field_size + c] + ', ' print(ship_string[:-2]) - #print(blocked_string[:-2] + ' ' + ship_string[:-2]) + # 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 + diff --git a/HNS/MB/ShipMode.py b/HNS/MB/ShipMode.py new file mode 100644 index 0000000..741f104 --- /dev/null +++ b/HNS/MB/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/HNS/MB/main.py b/HNS/MB/main.py index c8e80f4..90720e7 100644 --- a/HNS/MB/main.py +++ b/HNS/MB/main.py @@ -1,4 +1,8 @@ +import json +import os from tkinter import * +from tkinter import filedialog + from ShipField import ShipField my_field = ShipField() @@ -34,10 +38,13 @@ def colorize(field, buttons): bg = 'blue' if "+" in field[i]: bg = 'orange' + if "r" in field[i]: + bg = 'red' buttons[i].configure(bg=bg) def keypress_handler(e): + global active_field if e.keysym.isnumeric(): active_field.set_ship_size(e.keysym) else: @@ -75,24 +82,44 @@ def button_enter(buttons, row, col): colorize(enemy_field, enemy_buttons) +def savebutton_click(event): + file_path = filedialog.asksaveasfilename(filetypes=[("JSON files", "*.json")]) + + if file_path: + with open(file_path, 'w') as f: + json.dump({'my_field': my_field}, f, default=ShipField.convert_to_json) + + +def loadbutton_click(event): + global my_field + + file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")]) + + if os.path.isfile(file_path): + with open(file_path) as lines: + my_field.from_json(json.load(lines)["my_field"]) + + colorize(my_field, my_buttons) + + window = Tk() window.title("Ship Craft!") -window.geometry('940x410') +window.geometry('940x510') window.bind_all('', keypress_handler) -my_field.toggle_ship_direction() -my_field.set_ship_size(4) -my_field.set_ship(1, 1) -my_field.toggle_ship_direction() -my_field.set_ship_size(3) -my_field.set_ship(0, 6) -my_field.set_ship_size(1) -my_field.set_ship(7, 3) - my_buttons = draw_field(window, my_field, 0) enemy_buttons = draw_field(window, enemy_field, 11) lbl = Label(window, text='', width=5, height=2) lbl.grid(column=10, row=0) +savebutton = Button(window, text='Save', width=20, height=2) +savebutton.bind('', savebutton_click) +savebutton.grid(column=0, row=11, columnspan=4) + +loadbutton = Button(window, text='Load', width=20, height=2) +loadbutton.bind('', loadbutton_click) +loadbutton.grid(column=5, row=11, columnspan=4) + window.mainloop() + diff --git a/HNS/MB/test_ShipField.py b/HNS/MB/test_ShipField.py new file mode 100644 index 0000000..0c9459b --- /dev/null +++ b/HNS/MB/test_ShipField.py @@ -0,0 +1,251 @@ +from unittest import TestCase +from ShipField import ShipField +from ShootResult import ShootResult +from ShipDirection import ShipDirection +from ShipMode import ShipMode + + +class TestShipField(TestCase): + + def test_set_ship_size(self): + ship_field = ShipField() # Заводим объект типа ShipField + + ship_field.set_ship_size(1) + self.assertEqual(ship_field.ship_size, 1) + ship_field.set_ship_size(2) + self.assertEqual(ship_field.ship_size, 2) + ship_field.set_ship_size(3) + self.assertEqual(ship_field.ship_size, 3) + ship_field.set_ship_size(4) + self.assertEqual(ship_field.ship_size, 4) + + def test_set_ship_size_outofrange(self): + ship_field = ShipField() # Заводим объект типа ShipField + ship_field.set_ship_size(1) + + ship_field.set_ship_size(-1) + self.assertEqual(ship_field.ship_size, 1) + ship_field.set_ship_size(0) + self.assertEqual(ship_field.ship_size, 1) + ship_field.set_ship_size(6) + self.assertEqual(ship_field.ship_size, 1) + ship_field.set_ship_size(100) + self.assertEqual(ship_field.ship_size, 1) + + def test_set_ship_size_wrongtype(self): + ship_field = ShipField() # Заводим объект типа ShipField + ship_field.set_ship_size(1) + + ship_field.set_ship_size([]) + self.assertEqual(ship_field.ship_size, 1) + ship_field.set_ship_size('') + self.assertEqual(ship_field.ship_size, 1) + ship_field.set_ship_size(None) + self.assertEqual(ship_field.ship_size, 1) + ship_field.set_ship_size(False) + self.assertEqual(ship_field.ship_size, 1) + + def test_toggle_field_mode(self): + ship_field = ShipField() # Заводим объект типа ShipField + self.assertEqual(ship_field.field_mode, ShipMode.PUT) # Проверяем, что изначальное значение field_mode равно 0 + + ship_field.toggle_field_mode() # Вызываем метод, который тестируем + self.assertEqual(ship_field.field_mode, ShipMode.SHOOT) # Проверяем, что field_mode принял желаемое значение + + ship_field.toggle_field_mode() # Вызываем метод, который тестируем + self.assertEqual(ship_field.field_mode, ShipMode.PUT) # Проверяем, что field_mode принял желаемое значение + + def test_action(self): + self.fail() + + def test_target(self): + self.fail() + + def test_clear_marker(self): + ship_field = ShipField() + ship_field.field[0] = 'p' + ship_field.field[ship_field.field_size - 1] = 'p' + ship_field.field[ship_field.field_size - 4] = 'r' + ship_field.clear_marker() + + self.assertNotIn('p', ship_field.field) + self.assertNotIn('r', ship_field.field) + + def test_shoot_empty(self): + ship_field = ShipField() + + self.assertEqual(ship_field.field[0].strip(), '') + result = ship_field.shoot(0, 0) + + self.assertEqual(ship_field.field[0].strip(), '0') + self.assertEqual(result, ShootResult.EMPTY) + + def test_shoot_ship(self): + ship_field = ShipField() + + ship_field.field[0] = '1' + result = ship_field.shoot(0, 0) + + self.assertEqual(ship_field.field[0].strip(), '\\') + self.assertEqual(result, ShootResult.DAMAGED) + + def test_shoot_unknown(self): + ship_field = ShipField() + + ship_field.field[0] = 'x' + result = ship_field.shoot(0, 0) + + self.assertEqual(ship_field.field[0].strip(), 'x') + self.assertEqual(result, ShootResult.UNDEFINED) + + def test_shoot_outofrange(self): + ship_field = ShipField() + old_field_string = str.join(' ', ship_field.field) + + result = ship_field.shoot(-1, -1) + self.assertEqual(result, ShootResult.UNDEFINED) + + result = ship_field.shoot(-1, 0) + self.assertEqual(result, ShootResult.UNDEFINED) + + result = ship_field.shoot(1000, 1000) + self.assertEqual(result, ShootResult.UNDEFINED) + + result = ship_field.shoot(0, 1000) + self.assertEqual(result, ShootResult.UNDEFINED) + + new_field_string = str.join(' ', ship_field.field) + self.assertEqual(new_field_string, old_field_string) + + def test_check_possible_false(self): + # arrangement установка + ship_field = ShipField() + ship_field.set_ship_size(4) + ship_field.set_ship_direction(ShipDirection.HORIZONTAL) + + # action действие + ship_field.set_ship(5, 3) + + # assertion проверка занятых + self.assertEqual(ship_field.check_possible(5, 3), False) + self.assertEqual(ship_field.check_possible(5, 4), False) + self.assertEqual(ship_field.check_possible(5, 5), False) + self.assertEqual(ship_field.check_possible(5, 6), False) + + # проверка строки ниже + self.assertEqual(ship_field.check_possible(6, 3), False) + self.assertEqual(ship_field.check_possible(6, 4), False) + self.assertEqual(ship_field.check_possible(6, 5), False) + self.assertEqual(ship_field.check_possible(6, 6), False) + + # проверка строки выше + self.assertEqual(ship_field.check_possible(4, 3), False) + self.assertEqual(ship_field.check_possible(4, 4), False) + self.assertEqual(ship_field.check_possible(4, 5), False) + self.assertEqual(ship_field.check_possible(4, 6), False) + + def test_check_possible_true(self): + # arrangement установка + ship_field = ShipField() + ship_field.set_ship_size(4) + ship_field.set_ship_direction(ShipDirection.HORIZONTAL) + + # action действие + ship_field.set_ship(5, 3) + + # проверка свободных ниже на 2 строки + self.assertEqual(ship_field.check_possible(7, 3), True) + self.assertEqual(ship_field.check_possible(7, 4), True) + self.assertEqual(ship_field.check_possible(7, 5), True) + self.assertEqual(ship_field.check_possible(7, 6), True) + + # проверка свободных выше на 2 строки + self.assertEqual(ship_field.check_possible(3, 3), True) + self.assertEqual(ship_field.check_possible(3, 4), True) + self.assertEqual(ship_field.check_possible(3, 5), True) + self.assertEqual(ship_field.check_possible(3, 6), True) + + def test_check_blocked(self): + # arrangement установка + ship_field = ShipField() + ship_field.set_ship_size(4) + ship_field.set_ship_direction(ShipDirection.HORIZONTAL) + # action действие + ship_field.set_ship(5, 3) + # assertion проверка занятых + self.assertEqual(ship_field.check_blocked(5, 3), False) + self.assertEqual(ship_field.check_blocked(5, 4), False) + self.assertEqual(ship_field.check_blocked(5, 5), False) + self.assertEqual(ship_field.check_blocked(5, 6), False) + #проверка строки ниже + self.assertEqual(ship_field.check_blocked(6, 3), False) + self.assertEqual(ship_field.check_blocked(6, 4), False) + self.assertEqual(ship_field.check_blocked(6, 5), False) + self.assertEqual(ship_field.check_blocked(6, 7), False) + #проверка свободных ниже на 2 строки + self.assertEqual(ship_field.check_blocked(7, 3), True) + self.assertEqual(ship_field.check_blocked(7, 4), True) + self.assertEqual(ship_field.check_blocked(7, 5), True) + self.assertEqual(ship_field.check_blocked(7, 6), True) + + def test_set_ship_direction(self): + ship_field = ShipField() # Заводим объект типа ShipField + ship_field.set_ship_direction(ShipDirection.VERTICAL) + self.assertEqual(ship_field.ship_direction, ShipDirection.VERTICAL) + + ship_field.set_ship_direction(ShipDirection.HORIZONTAL) + self.assertEqual(ship_field.ship_direction, ShipDirection.HORIZONTAL) + + def test_set_ship_direction_outofrange(self): + ship_field = ShipField() # Заводим объект типа ShipField + ship_field.set_ship_direction(1) + + ship_field.set_ship_direction(-1) + ship_field.set_ship_direction(2) + self.assertEqual(ship_field.ship_direction, ShipDirection.VERTICAL) + + def test_set_ship_direction_wrongtype(self): + ship_field = ShipField() # Заводим объект типа ShipField + ship_field.set_ship_direction(1) + + ship_field.set_ship_direction(None) + ship_field.set_ship_direction([2]) + ship_field.set_ship_direction({}) + self.assertEqual(ship_field.ship_direction, ShipDirection.VERTICAL) + + def test_toggle_ship_direction(self): + # arrangement установка + ship_field = ShipField() + ship_field.set_ship_direction(ShipDirection.HORIZONTAL) + # action действие + ship_field.toggle_ship_direction() + # assertion проверка + self.assertEqual(ship_field.ship_direction, ShipDirection.VERTICAL) + + ship_field.toggle_ship_direction() + self.assertEqual(ship_field.ship_direction, ShipDirection.HORIZONTAL) + + def test_set_ship(self): + # arrangement установка + ship_field = ShipField() + ship_field.set_ship_size(4) + ship_field.set_ship_direction(ShipDirection.HORIZONTAL) + # action действие + ship_field.set_ship(5, 3) + # assertion проверка + self.assertEqual(ship_field.field[53].strip(), '1') + self.assertEqual(ship_field.field[54].strip(), '1') + self.assertEqual(ship_field.field[55].strip(), '1') + self.assertEqual(ship_field.field[56].strip(), '1') + + def test_set_ship_size4_vertical_outofrange(self): + # arrangement установка + ship_field = ShipField() + ship_field.set_ship_size(4) + ship_field.set_ship_direction(ShipDirection.VERTICAL) + old_field_string = str.join(" ", ship_field.field) + # action действие + ship_field.set_ship(7, 3) + # assertion проверка + new_field_string = str.join(" ", ship_field.field) + self.assertEqual(new_field_string, old_field_string) \ No newline at end of file