diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d26a9c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,116 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..37d0b49 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 BRNSystems + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d68fe27 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Bendzove_Piskvorky + A small network game diff --git a/local.py b/local.py new file mode 100644 index 0000000..deff7d0 --- /dev/null +++ b/local.py @@ -0,0 +1,83 @@ +width = 9 +height = 6 + +grid = [] + +for _ in range(width): + temp_grid = [] + for _ in range(height): + temp_grid.append(" ") + grid.append(temp_grid) + +player = 0 + + +def apply_gravity(gridx): + for gr in gridx: + for _ in range(height): + for i in range(height - 2, -1, -1): + if gr[i] == "X" and gr[i + 1] == " ": + gr[i + 1] = "X" + gr[i] = " " + if gr[i] == "O" and gr[i + 1] == " ": + gr[i + 1] = "O" + gr[i] = " " + if gr[i] == "X" and gr[i + 1] == " ": + gr[i + 1] = "X" + gr[i] = " " + if gr[i] == "O" and gr[i + 1] == " ": + gr[i + 1] = "O" + gr[i] = " " + + +def add(gridx, col3): + try: + col = int(col3) + except ValueError: + return 2 + if -1 < col < width: + global player + if gridx[col][0] == " ": + if player == 1: + gridx[col][0] = "O" + player = 0 + return 1 + else: + gridx[col][0] = "X" + player = 1 + return 1 + else: + return 0 + return 2 + + +def draw(gridx): + tempstorage = "" + for _ in range((width * 2) + 1): + tempstorage = tempstorage + "_" + tempstorage = tempstorage + "\n|" + for i in range(0, width): + tempstorage = tempstorage + f"{i}|" + tempstorage = tempstorage + "\n|" + for y in range(0, height): + for x in range(0, width): + tempstorage = tempstorage + gridx[x][y] + "|" + tempstorage = tempstorage + "\n|" + tempstorage = tempstorage[:-1] + for _ in range((width * 2) + 1): + tempstorage = tempstorage + "¯" + print(tempstorage) + + +while True: + draw(grid) + col2 = input(f"It is player{player} 's turn, input column number from 0 to {width - 1}:") + success = add(grid, col2) + while success != 1: + if success == 2: + col2 = input(f"Oops you have entered '{col2}' which is not a valid input. Please try again player{player}:") + elif success == 0: + col2 = input(f"Oops you have entered '{col2}' which is full. Please try again player{player}:") + success = add(grid, col2) + apply_gravity(grid) + draw(grid) diff --git a/main.py b/main.py new file mode 100644 index 0000000..3d4ff38 --- /dev/null +++ b/main.py @@ -0,0 +1,84 @@ +import aioconsole +import websockets +import asyncio +import json + +IP = "localhost:8765" + +player_id = 0 +ws = websockets.connect("ws://" + IP) +connected = False + +got_message = False + + +async def connect(): + global ws + global connected + async with websockets.connect("ws://localhost:8765") as wsx: + ws = wsx + connected = True + while True: + await asyncio.sleep(10) + + +async def recieve_data_to_server(): + while not connected: + await asyncio.sleep(1) + global ws + global player_id + global got_message + while True: + async for message in ws: + try: + data = json.loads(message) + if data["action"] == "redraw": + print(data["data"]) + got_message = True + elif data["action"] == "info": + print(data["data"]) + got_message = True + elif data["action"] == "id": + player_id = data["id"] + if got_message: + print("Enter command:\n") + except Exception as e: + print(e) + print(message) + + +async def send_data_to_server(): + while not connected: + await asyncio.sleep(1) + global ws + global player_id + while True: + cmd = await aioconsole.ainput("Enter command:\n") + if cmd == "reset": + await ws.send(json.dumps({"action": "reset"})) + elif cmd == "redraw": + await ws.send(json.dumps({"action": "redraw"})) + elif cmd.startswith("resize"): + cmd_args = cmd.split(" ") + if len(cmd_args) >= 3: + await ws.send(json.dumps({"action": "resize", "width": cmd_args[1], "height": cmd_args[2]})) + else: + print("Incorrect argument number.") + elif cmd.startswith("add"): + cmd_args = cmd.split(" ") + if len(cmd_args) >= 2: + await ws.send(json.dumps({"action": "add", "col": cmd_args[1], "id": player_id})) + elif cmd == "help": + await ws.send(json.dumps({"action": "help"})) + else: + print("Unknown command. Type 'help' to list all commands.") + + +loop = asyncio.get_event_loop() + + +async def run_stuff(): + await asyncio.gather(connect(), recieve_data_to_server(), send_data_to_server()) + + +asyncio.run(run_stuff()) diff --git a/server.py b/server.py new file mode 100644 index 0000000..0ddea62 --- /dev/null +++ b/server.py @@ -0,0 +1,190 @@ +import asyncio +import json +import random +import websockets +import threading + +width = 9 +height = 6 +player_on_turn = 0 + +grid = [] + +connected = set() +cache = [{"player": 0, "col": -1}] + + +async def socket_broadcast(data): + for client in connected: + await client.send(str(data)) + + +players = [] + + +async def hello(ws, _): + global cache + global width + global height + global player_on_turn + i = 0 + connected.add(ws) + this_id = random.randint(0, 100000) + players.append({"ws": ws, "id": this_id}) + if len(players) >= 3: + await socket_broadcast(json.dumps({"action": "info", "data": f"Warning {len(players)} players are connected"})) + await ws.send(json.dumps({"action": "id", "id": this_id})) + try: + while i == 0: + try: + x = await ws.recv() + try: + temp = json.loads(x) + print(str(temp) + " data") + if temp["action"] == "reset": + await reset() + await draw(grid) + await socket_broadcast( + json.dumps({"action": "info", "data": f"It is player{player_on_turn} 's turn."})) + await players[player_on_turn]['ws'].send(json.dumps( + {"action": "info", "data": f"It is your turn, input column number from 0 to {width - 1}:"})) + elif temp["action"] == "redraw": + await ws.send(await draw(grid, True)) + elif temp["action"] == "resize": + try: + width = int(temp["width"]) + height = int(temp["height"]) + await reset() + await ws.send(await draw(grid, True)) + await socket_broadcast( + json.dumps({"action": "info", "data": f"It is player{player_on_turn} 's turn."})) + await players[player_on_turn]['ws'].send(json.dumps({"action": "info", + "data": f"It is your turn, type 'add (number of column from 0 to {width - 1}' :"})) + except ValueError: + await ws.send(json.dumps({"action": "info", "data": "Invalid arguments to resize."})) + elif temp["action"] == "add": + plx = 0 + for player in players: + if temp["id"] == player["id"]: + if plx == player_on_turn: + res = await add(grid, temp["col"]) + if res == 1: + await apply_gravity(grid) + await socket_broadcast(await draw(grid, True)) + await socket_broadcast(json.dumps( + {"action": "info", "data": f"It is player{player_on_turn} 's turn."})) + await players[player_on_turn]['ws'].send(json.dumps({"action": "info", + "data": f"It is your turn, input column number from 0 to {width - 1}:"})) + elif res == 2: + await players[player_on_turn]['ws'].send(json.dumps({"action": "info", + "data": f"Oops you have entered '{temp['col']}' which is not a valid input. Please try again player{player_on_turn}:"})) + elif res == 0: + await players[player_on_turn]['ws'].send(json.dumps({"action": "info", + "data": f"Oops you have entered '{temp['col']}' which is full. Please try again player{player_on_turn}:"})) + plx = plx + 1 + elif temp["action"] == "help": + await ws.send(json.dumps({"action": "info", + "data": f"\n'reset' - Resets the game, also you must start the game with this\n'redraw' - Sends you the current state of th board\n'resize' - Resizes the playing board and resets it usage: 'resize (width) (height)'\n'add' - Adds your character to a column, usage: 'add (column number from 0 to {width - 1})\n'help' - Prints this help message"})) + + except Exception as e: + print(e) + except Exception as e: + print(e) + connected.remove(ws) + i = 1 + finally: + # Unregister. + try: + connected.remove(ws) + i = 0 + for x in players: + if x[ws] == ws: + players.pop(i) + i = i + 1 + except Exception as e: + print(e) + + +async def reset(): + global grid + grid = [] + for _ in range(width): + temp_grid = [] + for _ in range(height): + temp_grid.append(" ") + grid.append(temp_grid) + + +async def apply_gravity(gridx): + for gr in gridx: + for _ in range(height): + for i in range(height - 2, -1, -1): + if gr[i] == "X" and gr[i + 1] == " ": + gr[i + 1] = "X" + gr[i] = " " + if gr[i] == "O" and gr[i + 1] == " ": + gr[i + 1] = "O" + gr[i] = " " + if gr[i] == "X" and gr[i + 1] == " ": + gr[i + 1] = "X" + gr[i] = " " + if gr[i] == "O" and gr[i + 1] == " ": + gr[i + 1] = "O" + gr[i] = " " + + +async def add(gridx, col3): + try: + col = int(col3) + except ValueError: + return 2 + if -1 < col < width: + global player_on_turn + if gridx[col][0] == " ": + if player_on_turn == 1: + gridx[col][0] = "O" + player_on_turn = 0 + return 1 + else: + gridx[col][0] = "X" + player_on_turn = 1 + return 1 + else: + return 0 + return 2 + + +async def draw(gridx, return_data=False): + temporary_storage = "" + for _ in range((width * 2) + 1): + temporary_storage = temporary_storage + "_" + temporary_storage = temporary_storage + "\n|" + for i in range(0, width): + temporary_storage = temporary_storage + f"{i}|" + temporary_storage = temporary_storage + "\n|" + for y in range(0, height): + for x in range(0, width): + temporary_storage = temporary_storage + gridx[x][y] + "|" + temporary_storage = temporary_storage + "\n|" + temporary_storage = temporary_storage[:-1] + for _ in range((width * 2) + 1): + temporary_storage = temporary_storage + "¯" + print(temporary_storage) + if return_data: + return json.dumps({"action": "redraw", "data": temporary_storage}) + else: + await socket_broadcast(json.dumps({"action": "redraw", "data": temporary_storage})) + + +start_server = websockets.serve(hello, "localhost", 8765) + +loop = asyncio.get_event_loop() + + +def loop_in_thread(): + loop.run_until_complete(start_server) + loop.run_forever() + + +thread = threading.Thread(loop_in_thread()) +thread.start()