1
0
forked from MDL29/JacoBot

Compare commits

...

22 Commits

Author SHA1 Message Date
d22cb7308a
📝 Add linter doc 2024-04-25 17:05:50 +02:00
fb94a8b8cd
Merge remote-tracking branch 'upstream/main' into 15_improve-README 2024-04-25 16:56:49 +02:00
041307a28c Add flake8 linter 2024-04-25 14:46:29 +00:00
21d334ddb9 Improve README 2024-04-25 16:07:38 +02:00
5008db05e1 Creates a garbage zone for the virtual pad #34
close MDL29/JacoBot#34
2024-04-25 15:47:50 +02:00
9513e5d5bc Disable DEBUG level log for Arcade
close #58
2024-04-25 11:33:39 +02:00
c1771906e4 Add Rich Library for logging in JacoVirt
close #56
2024-04-25 10:10:36 +02:00
e31eec876a Add command & subcommand for JacoVirt via argparse
* Command `jacovirt`
* Sub command `jacovirt pad`
* Sub command `jacovirt bot`

close #20
2024-04-24 23:53:11 +02:00
ed319f0a2e Move the token of the virtual pad #51
MDL29/JacoBot#51
2024-04-24 17:28:19 +02:00
mdl29
bd926a629d window displayed 2024-04-24 16:34:01 +02:00
d9b25436e9 Improve readme for JacoVirt Python project #12
MDL29/JacoBot#12
2024-04-24 16:19:34 +02:00
b5000a668d Create a generator of token for the virtual pad #31
MDL29/JacoBot#31
2024-04-24 14:43:13 +02:00
8a60d943fd Create token sprite
MDL29/JacoBot#29
2024-04-24 11:32:04 +02:00
0708cfb319 Create instructions with Pydantic
MDL29/JacoBot#17
2024-04-24 11:08:04 +02:00
df30c40deb Add logger for the virtual pad #41
MDL29/JacoBot#29
2024-04-24 10:32:22 +02:00
7e016819dc Add .gitignore 2024-04-23 22:35:08 +02:00
mdl29
3db0ceaab3 Organisation of JacoVirt Bot 2024-04-23 13:32:47 +00:00
437f336e5a Create the main window for the virtual pad MDL29/JacoBot#29 2024-04-23 15:06:51 +02:00
mdl29
dab02d2aeb Receive instructions from Redis
MDL29/JacoBot#19
2024-04-23 14:26:27 +02:00
mdl29
6c4c40ed75 Improve JacoVirt Bot with dark & light theme 2024-04-23 14:24:24 +02:00
7ba83ee570 Change path system 2024-04-23 11:46:11 +02:00
e52488249f Send instructions from JacoPad via Redis MDL29/JacoBot#18 2024-04-23 10:18:25 +02:00
33 changed files with 572 additions and 404 deletions

232
.gitignore vendored Normal file
View File

@ -0,0 +1,232 @@
### C++ ###
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
### PlatformIO ###
.pioenvs
.piolibdeps
.clang_complete
.gcc-flags.json
.pio
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-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
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# 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/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
### Vim ###
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

View File

@ -1 +1,26 @@
# JacoVirt
## How run the project ?
Clone the project :
~~~
git clone https://git.afpy.org/MDL29/JacoBot.git
~~~
Go in the folder of project then in the virtual pad folder
~~~
cd Jacobot/jacovirt
~~~
Downloads dependencies and create a virtual environement
~~~
poetry install
~~~
Run the virtual pad with :
~~~
poetry run jacovirt pad
~~~
Run the virtual bot with :
~~~
poetry run jacovirt bot
~~~
> If you use JacoVirt for dev, **you should test your code with the PEP8 linter :** `poetry run flake8`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

56
jacovirt/jacovirt/args.py Normal file
View File

@ -0,0 +1,56 @@
import argparse
import logging
import jacovirt.logger
log_level = {
"CRITICAL": logging.CRITICAL,
"ERROR": logging.ERROR,
"WARN": logging.WARNING,
"INFO": logging.INFO,
"DEBUG": logging.DEBUG,
}
def main():
# General parser
parser = argparse.ArgumentParser(description=__doc__)
# Subparser
sub = parser.add_subparsers(dest="sub")
parser.add_argument(
"-l",
"--log",
choices=log_level.keys(),
default="WARN",
help=("Set level log"),
dest="log",
)
# Parser for JacoVirt Pad
pad = sub.add_parser("pad", help="Run JacoVirt Pad")
# Parser for JacoVirt Bot
bot = sub.add_parser("bot", help="Run JacoVirt Bot")
args = parser.parse_args()
if args.sub is None:
parser.print_help()
exit(1)
logging.getLogger().setLevel(args.log)
match args.sub:
case "pad":
logging.info("Lunch JacoVirt Pad")
from jacovirt.pad import window
window.main()
case "bot":
logging.info("Lunch JacoVirt Bot")
from jacovirt.bot import window
window.main()
case _:
SystemExit

View File

@ -0,0 +1,46 @@
import arcade
import jacovirt.logger
import logging
# Screen title and size
SCREEN_TITLE = "JacoVirt Bot"
SCREEN_MULTILPLIER = 1
SCREEN_WIDTH = int(1000 * SCREEN_MULTILPLIER)
SCREEN_HEIGHT = int(1000 * SCREEN_MULTILPLIER)
# Window color
BACKGROUND_COLOR = arcade.color.CATALINA_BLUE
class Bot(arcade.Window):
"""Main application class"""
def __init__(self):
# Init parent class
super().__init__(int(SCREEN_WIDTH), int(SCREEN_HEIGHT), SCREEN_TITLE)
# Set background color
arcade.set_background_color(BACKGROUND_COLOR)
def setup(self):
"""Set up the pad"""
logging.info("Set up the bot.")
def on_draw(self):
"""Render the screen"""
# Clear the screen
self.clear()
def on_key_press(self, symbol, modifiers):
"""Called when the user presses key"""
if symbol == arcade.key.R:
self.setup()
print("Restart !")
if symbol == arcade.key.Q:
arcade.exit()
def main():
"""Main method"""
window = Bot()
window.setup()
arcade.run()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 414 KiB

View File

@ -0,0 +1,9 @@
from datetime import datetime
from typing import Literal
from pydantic import BaseModel
class Instruction(BaseModel):
move: Literal["FORWARD", "LEFT", "RIGHT"]
timestamp: datetime
source: str

View File

@ -1,76 +0,0 @@
from turtle import *
"""host = "192.168.20.49"
channel = "LPH"
# Connexion à Redis
r = redis.Redis(host=host)
p = r.pubsub()
p.psubscribe(channel)
"""
def main():
angle = 0
def cubetto_init():
bgpic("fond.png")
register_shape("Cubetto_forme_haut.gif")
register_shape("Cubetto_forme_bas.gif")
register_shape("Cubetto_forme_gauche.gif")
register_shape("Cubetto_forme_droite.gif")
shape("Cubetto_forme_droite.gif")
shapesize(3,3,6)
penup()
goto(-210,194)
def cubetto_forme_init():
global angle
if angle == 360 or angle == -360:
angle=0
if angle == 0:
shape("Cubetto_forme_droite.gif")
if angle == 90 or angle == -270:
shape("Cubetto_forme_haut.gif")
if angle == 180 or angle == -180:
shape("Cubetto_forme_gauche.gif")
if angle == 270 or angle == -90:
shape("Cubetto_forme_bas.gif")
def cubetto_forward():
forward(84)
def cubetto_left():
global angle
left(90)
angle += 90
cubetto_forme_init()
def cubetto_right():
global angle
right(90)
angle -= 90
cubetto_forme_init()
def cubetto_backward():
right(180)
forward(84)
right(180)
cubetto_init()
while True:
message = []
if message == "FORDWARD":
cubetto_forward()
elif message == "LEFT":
cubetto_left()
elif message == "RIGHT":
cubetto_right()
elif message == "i":
cubetto_init()
elif message == "FUNCTION":
pass

View File

@ -1,326 +0,0 @@
import arcade
import os.path
SCREEN_MULTILPLIER = 2
SCREEN_MULTILPLIER /= 4
# Screen title and size
SCREEN_TITLE = "Jaco Pad"
SCREEN_WIDTH = int(2000* SCREEN_MULTILPLIER)
SCREEN_HEIGHT = int(2000* SCREEN_MULTILPLIER)
#Token row
TOKEN_ROW = 8
# Token size
TOKEN_HEIGHT = int(160 * SCREEN_MULTILPLIER)
TOKEN_WIDTH = int(160 * SCREEN_MULTILPLIER)
# Token scale
HELD_TOKEN_SCALE_MULTILPLIER = 1.4
# Space between tokens
X_SPACING_TOKEN = int(45*SCREEN_MULTILPLIER + TOKEN_WIDTH)
Y_SPACING_TOKEN = int(45*SCREEN_MULTILPLIER + TOKEN_HEIGHT)
# Token start
X_TOKEN_START = int(108*SCREEN_MULTILPLIER + TOKEN_WIDTH / 2)
Y_TOKEN_START = int(1618*SCREEN_MULTILPLIER + TOKEN_HEIGHT / 2)
# List of token types
TOKEN_TYPES = ["up", "left", "right", "function"]
#Mat start
X_MAT_START = int(1072 * SCREEN_MULTILPLIER)
Y_MAT_START = int(1648 * SCREEN_MULTILPLIER)
X_MAT_FUNCTION_START = X_MAT_START
Y_MAT_FUNCTION_START = int(510 * SCREEN_MULTILPLIER)
# Mat size
MAT_HEIGHT = int(200 * SCREEN_MULTILPLIER)
MAT_WIDTH = int(200 * SCREEN_MULTILPLIER)
# Number of column & row mat
MAT_COLUMN = 4
MAT_ROW = 4
MAT_FUNCTION_ROW = 2
# Space between mats
X_SPACING_MAT = int(MAT_WIDTH + 30 * SCREEN_MULTILPLIER)
Y_SPACING_MAT = int(MAT_WIDTH + 30 * SCREEN_MULTILPLIER)
# Image
X_START_MAIN_MAT_IMAGE = 1415 * SCREEN_MULTILPLIER
Y_START_MAIN_MAT_IMAGE = 1304 * SCREEN_MULTILPLIER
X_START_FUNCTION_MAT_IMAGE = X_START_MAIN_MAT_IMAGE
Y_START_FUNCTION_MAT_IMAGE = 393 * SCREEN_MULTILPLIER
# Couleur
BACKGROUND_COLOR = (133, 100, 100)
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
class Start_mat(arcade.SpriteSolidColor):
def __init__(self, height, width, color=arcade.color.AMBER):
super().__init__(width, height, color)
class Mat(arcade.SpriteSolidColor):
def __init__(self, height, width, color=arcade.color.AMBER):
super().__init__(width, height, color)
class Mat_function(arcade.SpriteSolidColor):
def __init__(self, height, width, color=arcade.color.AMBER):
super().__init__(width, height, color)
class Token_sprite(arcade.Sprite):
""" Token sprite """
def __init__(self, token_type, scale=1):
# Attributes for token type
self.token_type = token_type
scale *= SCREEN_MULTILPLIER
self.image_file_name = f"{CURRENT_PATH}/Img/token/{self.token_type}.png"
# Call the parent
super().__init__(self.image_file_name, scale, hit_box_algorithm="None")
class Image(arcade.Sprite):
def __init__(self, file_name, scale=1):
self.image_file_name = f"{CURRENT_PATH}/Img/{file_name}"
scale *= SCREEN_MULTILPLIER
# Call the parent
super().__init__(self.image_file_name, scale, hit_box_algorithm="None")
class Cubito(arcade.Window):
"""Main application class"""
def __init__(self):
# Init parent class
super().__init__(int(SCREEN_WIDTH), int(SCREEN_HEIGHT), SCREEN_TITLE)
# Set background color
arcade.set_background_color(BACKGROUND_COLOR)
# List of tokens
self.token_list = None
# Hold token
self.held_token = None
# Origin pos for hold token
self.held_token_original_position = None
#List of start mats
self.start_mat_list = None
# List of mats
self.mat_list = None
# List of mats function
self.mat_function_list = None
# List of mats
self.image_list = None
def setup(self):
"""Set up the game"""
# Token we are dragging with the mouse
self.held_token = None
# Original location of token we are dragging with the mouse in case
# they have to go back.
self.held_token_original_position = None
self.token_list = arcade.SpriteList()
self.mat_list = arcade.SpriteList()
self.start_mat_list = arcade.SpriteList()
self.mat_function_list = arcade.SpriteList()
self.image_list = arcade.SpriteList()
for y in range(TOKEN_ROW):
x = X_TOKEN_START
for token_type in TOKEN_TYPES:
#placer les tokens
token = Token_sprite(token_type)
token.position = x, Y_TOKEN_START - Y_SPACING_TOKEN * y
x += X_SPACING_TOKEN
self.token_list.append(token)
token.token_type
#placer des cases sous les tokens
start_mat = Start_mat(MAT_HEIGHT, MAT_WIDTH, color=arcade.color.BEIGE)
start_mat.position = x - X_SPACING_TOKEN, Y_TOKEN_START - Y_SPACING_TOKEN * y
self.start_mat_list.append(start_mat)
# Placer les cases principales
for y in range(MAT_ROW):
for x in range(MAT_COLUMN):
mat = Mat(MAT_HEIGHT, MAT_WIDTH)
mat.position = X_MAT_START + X_SPACING_MAT * x, Y_MAT_START - Y_SPACING_MAT * y
self.mat_list.append(mat)
# Placer les cases fonctions
for y in range(MAT_FUNCTION_ROW):
for x in range(MAT_COLUMN):
mat = Mat_function(MAT_HEIGHT, MAT_WIDTH, arcade.color.BABY_BLUE)
mat.position = X_MAT_FUNCTION_START + X_SPACING_MAT * x, Y_MAT_FUNCTION_START - Y_SPACING_MAT * y
self.mat_function_list.append(mat)
# Placer les images
image = Image(file_name="case/Principal.png", scale=1)
image.position = X_START_MAIN_MAT_IMAGE, Y_START_MAIN_MAT_IMAGE
self.image_list.append(image)
image = Image(file_name="case/Fonction.png", scale=1)
image.position = X_START_FUNCTION_MAT_IMAGE, Y_START_FUNCTION_MAT_IMAGE
self.image_list.append(image)
def on_draw(self):
"""Render the screen"""
# Clear the screen
self.clear()
#Draw the images
self.image_list.draw()
#Draw the mat
# self.mat_list.draw()
# self.start_mat_list.draw()
# self.mat_function_list.draw()
# Draw the token
self.token_list.draw()
def pull_to_top(self, token: arcade.Sprite):
""" Pull token to top of rendering order (last to render, looks on-top) """
# Remove, and append to the end
self.token_list.remove(token)
self.token_list.append(token)
def on_mouse_press(self, x, y, button, key_modifiers):
"""Called when the user presses a mouse button"""
# Get list of tokens we've clicked on
tokens = arcade.get_sprites_at_point((x, y), self.token_list)
# Have we clicked on a token?
if len(tokens) > 0:
# Might be a stack of tokens, get the top one
primary_token = tokens[-1]
# All other cases, grab the token we are clicking on
self.held_token = primary_token
# Save the position
self.held_token_original_position = self.held_token.position
# Expand the size of token
self.held_token.scale *= HELD_TOKEN_SCALE_MULTILPLIER
# Put on top in drawing order
self.pull_to_top(self.held_token)
def on_mouse_release(self, x, y, button, modifiers):
"""Called when the user presses a mouse button"""
def collision(reset_position, list_mat):
# Find the closest mat, in case we are in contact with more than one
mat, distance = arcade.get_closest_sprite(self.held_token, list_mat)
# See if we are in contact with the closest mat
if arcade.check_for_collision(self.held_token, mat):
# Reduce the size of token
self.held_token.scale /= HELD_TOKEN_SCALE_MULTILPLIER
# Center the token
self.held_token.position = mat.center_x, mat.center_y
# Success, don't reset position of tokens
reset_position = False
return reset_position
# If we don't have any tokens, who cares
if self.held_token == None:
return
reset_position = True
reset_position = collision(reset_position, self.mat_list)
reset_position = collision(reset_position, self.start_mat_list)
reset_position = collision(reset_position, self.mat_function_list)
if reset_position:
# Where-ever we were dropped, it wasn't valid. Reset the each token's position
# to its original spot.
self.held_token.position = self.held_token_original_position
# Reduce the size of token
self.held_token.scale /= HELD_TOKEN_SCALE_MULTILPLIER
# We are no longer holding tokens
self.held_token = None
def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
"""Called when the user moves the mouse"""
# If we are holding token, move it with the mouse
if self.held_token != None:
self.held_token.center_x += dx
self.held_token.center_y += dy
def on_key_press(self, symbol, modifiers):
"""Called when the user presses key"""
if symbol == arcade.key.R:
self.setup()
cubito.reset()
print("Restart !")
if symbol == arcade.key.Q:
arcade.exit()
if symbol == arcade.key.S:
self.cubito()
def cubito(self, function=False):
"""Move cubito !"""
if function:
list = self.mat_function_list
else:
list = self.mat_list
for mat in list:
token, distance = arcade.get_closest_sprite(mat, self.token_list)
if arcade.check_for_collision(token, mat):
token_type = str(token.token_type)
if token_type == "up":
cubito.fordward()
if token_type == "left":
cubito.left()
if token_type == "right":
cubito.right()
if token_type == "function":
if function:
return
else:
self.cubito(function=True)
def main():
"""Main method"""
window = Cubito()
window.setup()
arcade.run()

View File

@ -0,0 +1,8 @@
import logging
from rich.logging import RichHandler
# Set level INFO log for Arcade library
logging.getLogger("arcade").setLevel(logging.INFO)
# Logger config
logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s", datefmt="%m/%d/%Y %H:%M:%S", handlers=[RichHandler()])

View File

@ -0,0 +1,40 @@
import arcade
from importlib import resources
class Token(arcade.Sprite):
""" Token sprite """
def __init__(self, token_type, scale=1):
# Attributes for token type
self.token_type = token_type
with resources.path("jacovirt", 'Img') as img_folder :
self.image_file_name = f"{img_folder}/pad/token/{self.token_type}.png"
# Call the parent
super().__init__(self.image_file_name, scale, hit_box_algorithm="None")
class Token_generator(arcade.Sprite):
""" Token generator sprite """
def __init__(self, token_type, scale=1):
# Attributes for token type
self.type = token_type
self.image_file_name = ":resources:images/tiles/boxCrate_double.png"
# Call the parent
super().__init__(self.image_file_name, scale, hit_box_algorithm="None")
def create_token(self, x, y):
# Create a token
token = Token(self.type)
token.position = x, y
return token
class Token_trash(arcade.SpriteSolidColor):
def __init__(self, height, width, color=arcade.color.AMERICAN_ROSE):
super().__init__(width, height, color)

View File

@ -0,0 +1,148 @@
import arcade
import jacovirt.logger
import logging
from jacovirt.pad.token import Token, Token_generator, Token_trash
# Screen title and size
SCREEN_TITLE = "Jaco Pad"
SCREEN_MULTILPLIER = 1
SCREEN_WIDTH = int(1000* SCREEN_MULTILPLIER)
SCREEN_HEIGHT = int(1000* SCREEN_MULTILPLIER)
# Window color
BACKGROUND_COLOR = (133, 100, 100)
class Pad(arcade.Window):
"""Main application class"""
def __init__(self):
# Init parent class
super().__init__(int(SCREEN_WIDTH), int(SCREEN_HEIGHT), SCREEN_TITLE)
# Set background color
arcade.set_background_color(BACKGROUND_COLOR)
# List of token generator
self.tokens_generator = arcade.SpriteList()
# List of trash
self.tokens_trash = arcade.SpriteList()
# The held token
self.held_token = None
self.tokens = arcade.SpriteList()
def setup(self):
"""Set up the pad"""
logging.info("Set up the pad.")
# Create a token
token = Token("up", 1*SCREEN_MULTILPLIER)
token.position = 500, 500
self.tokens.append(token)
# Create a token generator
token_generator = Token_generator("up", 1*SCREEN_MULTILPLIER)
token_generator.position = 200, 500
self.tokens_generator.append(token_generator)
# Create a other token generator
token_generator = Token_generator("right", 1*SCREEN_MULTILPLIER)
token_generator.position = 800, 500
self.tokens_generator.append(token_generator)
# Create a trash for token
trash = Token_trash(200, 200)
trash.position = 500, 800
self.tokens_trash.append(trash)
def on_draw(self):
"""Render the screen"""
# Clear the screen
self.clear()
# Draw the token generator
self.tokens_generator.draw()
# Draw the trash
self.tokens_trash.draw()
# Draw the tokens
self.tokens.draw()
# Draw the held tokens
if self.held_token is not None:
self.held_token.draw()
def on_key_press(self, symbol, modifiers):
"""Called when the user presses key"""
if symbol == arcade.key.R:
self.setup()
print("Restart !")
if symbol == arcade.key.Q:
arcade.exit()
def on_mouse_press(self, x, y, button, key_modifiers):
"""Called when the user presses a mouse button"""
logging.info(f"Mouse pressed at {x},{y}")
# Get list of tokens we've clicked on
tokens = arcade.get_sprites_at_point((x, y), self.tokens)
# Have we clicked on a token?
if len(tokens) > 0:
# Might be a stack of tokens, get the top one
primary_token = tokens[-1]
self.tokens.remove(primary_token)
# All other cases, grab the token we are clicking on
self.held_token = primary_token
return
# Get the list of generator we've clicked on
generators = arcade.get_sprites_at_point((x, y), self.tokens_generator)
# Have we clicked on a generator ?
if len(generators) > 0:
generator = generators[-1]
logging.info(f"Cliced on the generator of {generator.type}")
# Create a token and take it
self.held_token = generator.create_token(x, y)
def on_mouse_release(self, x, y, button, modifiers):
"""Called when the user presses a mouse button"""
logging.info(f"Mouse relase at {x},{y}")
# Get list of tokens_trash we've release on
tokens_trash = arcade.get_sprites_at_point((x, y), self.tokens_trash)
# If we are on a tokens_trash
if len(tokens_trash) > 0:
# Delete the held token
self.held_token = None
# remove token from hand
if self.held_token != None:
self.tokens.append(self.held_token)
self.held_token = None
def on_mouse_motion(self, x: float, y: float, dx: float, dy: float):
"""Called when the user moves the mouse"""
# If we are holding token, move it with the mouse
if self.held_token != None:
self.held_token.center_x += dx
self.held_token.center_y += dy
def main():
"""Main method"""
window = Pad()
window.setup()
arcade.run()

View File

@ -8,13 +8,19 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.11"
arcade = "^2.6.17"
redis = "^5.0.3"
pydantic = "^2.7.1"
rich = "^13.7.1"
[tool.poetry.group.dev.dependencies]
flake8 = "^7.0.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
jacovirt-bot = "jacovirt.jacobot:main"
jacovirt-pad = "jacovirt.jacopad:main"
jacovirt = "jacovirt.args:main"
[[tool.poetry.include]]
path = "jacovirt/jacovirt/Img/*"