Added missing files and removed unnessesary ones
This commit is contained in:
207
Python/soper/soper.py
Executable file
207
Python/soper/soper.py
Executable file
@ -0,0 +1,207 @@
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum, auto
|
||||
from random import sample
|
||||
from typing import Any, Callable, Iterator, Self
|
||||
|
||||
from PyQt6 import QtGui
|
||||
from PyQt6.QtCore import QSize, Qt, QTimer
|
||||
from PyQt6.QtGui import QIcon
|
||||
from PyQt6.QtWidgets import (
|
||||
QApplication,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QLCDNumber,
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
class Cords:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
class ButtonType(StrEnum):
|
||||
EMPTY = auto()
|
||||
BOMB = auto()
|
||||
NUMBER = auto()
|
||||
|
||||
|
||||
class Button(QPushButton):
|
||||
def __init__(
|
||||
self,
|
||||
cords: Cords,
|
||||
handler1: Callable[[Self, bool], Any],
|
||||
handler2: Callable[[Self], Any],
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.setFixedSize(50, 50)
|
||||
self.type = ButtonType.EMPTY
|
||||
self.number = 0
|
||||
self.cords = cords
|
||||
self.marked: bool = False
|
||||
self.revealed: bool = False
|
||||
self.left_handler = handler1
|
||||
self.right_handler = handler2
|
||||
|
||||
def reveal(self) -> Iterator[Cords]:
|
||||
self.revealed = True
|
||||
self.setStyleSheet("background-color: grey")
|
||||
if self.type is ButtonType.NUMBER:
|
||||
self.setText(str(self.number))
|
||||
|
||||
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
|
||||
if e.button() is Qt.MouseButton.LeftButton:
|
||||
self.left_handler(self, True)
|
||||
elif e.button() is Qt.MouseButton.RightButton:
|
||||
self.right_handler(self)
|
||||
|
||||
def get_neighbors(self) -> Iterator[Cords]:
|
||||
for x in range(self.cords.x - 1, self.cords.x + 2):
|
||||
for y in range(self.cords.y - 1, self.cords.y + 2):
|
||||
if x < 0 or x >= 10:
|
||||
continue
|
||||
if y < 0 or y >= 10:
|
||||
continue
|
||||
if Cords(x, y) == self.cords:
|
||||
continue
|
||||
yield Cords(x, y)
|
||||
|
||||
def update_texture(self) -> None:
|
||||
if self.marked:
|
||||
self.setIcon(QIcon("flag.jpg"))
|
||||
self.setIconSize(QSize(50, 50))
|
||||
else:
|
||||
self.setIcon(QIcon())
|
||||
# TODO: remove
|
||||
if self.type is ButtonType.BOMB:
|
||||
self.setStyleSheet("background-color: red")
|
||||
|
||||
|
||||
class Window(QWidget):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.setGeometry(0, 0, 500, 500)
|
||||
self.seconds = 0
|
||||
self.revealed_buttons: set[Cords] = set()
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self.show_time)
|
||||
self.timer.setInterval(1000)
|
||||
self.timer.start()
|
||||
self.bomb_amount = 10
|
||||
self.flags_amount = 0
|
||||
self.setWindowTitle("Окно")
|
||||
self.create_buttons()
|
||||
self.select_bombs()
|
||||
|
||||
def create_buttons(self) -> None:
|
||||
vbox = QVBoxLayout()
|
||||
|
||||
hbox = QHBoxLayout()
|
||||
self.counter = QLabel(f"{self.flags_amount}/{self.bomb_amount}")
|
||||
finish_button = QPushButton()
|
||||
finish_button.setText("FINISH")
|
||||
finish_button.clicked.connect(self.finish)
|
||||
self.time = QLCDNumber()
|
||||
self.time.setDigitCount(5)
|
||||
self.time.display("00:00")
|
||||
hbox.addWidget(self.counter)
|
||||
hbox.addWidget(finish_button)
|
||||
hbox.addWidget(self.time)
|
||||
|
||||
vbox.addLayout(hbox)
|
||||
|
||||
self.buttons: list[list[Button]] = []
|
||||
for x in range(10):
|
||||
hbox = QHBoxLayout()
|
||||
row = []
|
||||
|
||||
for y in range(10):
|
||||
button = Button(
|
||||
Cords(x, y),
|
||||
self.button_clicked,
|
||||
self.set_flag,
|
||||
)
|
||||
row.append(button)
|
||||
hbox.addWidget(button)
|
||||
|
||||
self.buttons.append(row)
|
||||
vbox.addLayout(hbox)
|
||||
self.setLayout(vbox)
|
||||
|
||||
def select_bombs(self) -> None:
|
||||
buttons = [j for i in self.buttons for j in i]
|
||||
bombs = sample(buttons, self.bomb_amount)
|
||||
for bomb in bombs:
|
||||
bomb.type = ButtonType.BOMB
|
||||
# TODO: remove
|
||||
bomb.setStyleSheet("background-color: red")
|
||||
for bomb in bombs:
|
||||
for cords in bomb.get_neighbors():
|
||||
button = self.buttons[cords.x][cords.y]
|
||||
if button.type is ButtonType.BOMB:
|
||||
continue
|
||||
button.type = ButtonType.NUMBER
|
||||
button.number += 1
|
||||
|
||||
def button_clicked(self, button: Button, user: bool) -> None:
|
||||
if button.cords in self.revealed_buttons:
|
||||
return
|
||||
self.revealed_buttons.add(button.cords)
|
||||
if button.type is ButtonType.BOMB:
|
||||
if user:
|
||||
QMessageBox.warning(self, "You lost", "You lost")
|
||||
self.close()
|
||||
return
|
||||
button.reveal()
|
||||
if button.type is ButtonType.NUMBER:
|
||||
return
|
||||
for cords in button.get_neighbors():
|
||||
neighbor = self.buttons[cords.x][cords.y]
|
||||
self.button_clicked(neighbor, False)
|
||||
|
||||
def set_flag(self, button: Button) -> None:
|
||||
if button.revealed:
|
||||
return
|
||||
was_marked = button.marked
|
||||
button.marked = not was_marked
|
||||
button.update_texture()
|
||||
if was_marked:
|
||||
self.flags_amount -= 1
|
||||
else:
|
||||
self.flags_amount += 1
|
||||
self.counter.setText(f"{self.flags_amount}/{self.bomb_amount}")
|
||||
|
||||
def finish(self) -> None:
|
||||
buttons = [j for i in self.buttons for j in i]
|
||||
bomb = ButtonType.BOMB
|
||||
if not all(button.marked for button in buttons if button.type is bomb):
|
||||
QMessageBox.warning(self, "You lost", "You lost")
|
||||
self.close()
|
||||
return
|
||||
QMessageBox.information(self, "You won", "You won")
|
||||
self.close()
|
||||
|
||||
def show_time(self) -> None:
|
||||
self.seconds += 1
|
||||
minutes, seconds = divmod(self.seconds, 60)
|
||||
self.time.display(f"{minutes:0>2}:{seconds:0>2}")
|
||||
if minutes >= 5:
|
||||
QMessageBox.warning(self, "You lost", "You lost")
|
||||
self.close()
|
||||
return
|
||||
|
||||
|
||||
def main() -> None:
|
||||
app = QApplication(sys.argv)
|
||||
window = Window()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user