diff --git a/chatroom/chatroom/chatroom.py b/chatroom/chatroom/chatroom.py index 1e3701e6..41d372df 100644 --- a/chatroom/chatroom/chatroom.py +++ b/chatroom/chatroom/chatroom.py @@ -74,7 +74,7 @@ def index() -> rx.Component: ), rx.button("Send", on_click=State.send_message), ), - on_submit=State.send_message, + on_submit=lambda d: State.send_message(), ), width="60vw", align_items="left", @@ -104,7 +104,9 @@ async def broadcast_event(name: str, payload: t.Dict[str, t.Any] = {}) -> None: # Emit the event. responses.append( app.event_namespace.emit( - str(rx.constants.SocketEvent.EVENT), update.json(), to=state.get_sid() + str(rx.constants.SocketEvent.EVENT), + update.json(), + to=state.get_sid(), ), ) for response in responses: diff --git a/chatroom/requirements.txt b/chatroom/requirements.txt index bc6a7f2e..c8ade313 100644 --- a/chatroom/requirements.txt +++ b/chatroom/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 +reflex>=0.2.9 diff --git a/clock/clock/clock.py b/clock/clock/clock.py index 7fe97d85..cb05c752 100644 --- a/clock/clock/clock.py +++ b/clock/clock/clock.py @@ -1,7 +1,7 @@ """A Reflex example of a analog clock.""" import asyncio -from datetime import datetime +from datetime import datetime, timezone from typing import Any import reflex as rx @@ -18,6 +18,7 @@ "US/Pacific", "US/Eastern", ] +DEFAULT_ZONE = TIMEZONES[-2] def rotate(degrees: int) -> str: @@ -36,12 +37,28 @@ class State(rx.State): """The app state.""" # The time zone to display the clock in. - zone: str = "US/Pacific" + zone: str = rx.Cookie(DEFAULT_ZONE) # Whether the clock is running. running: bool = False - @rx.var + # The last updated timestamp + _now: datetime = datetime.fromtimestamp(0) + + @rx.cached_var + def valid_zone(self) -> str: + """Get the current time zone. + + Returns: + The current time zone. + """ + try: + pytz.timezone(self.zone) + except Exception: + return DEFAULT_ZONE + return self.zone + + @rx.cached_var def time_info(self) -> dict[str, Any]: """Get the current time info. @@ -50,7 +67,7 @@ def time_info(self) -> dict[str, Any]: Returns: A dictionary of the current time info. """ - now = datetime.now(pytz.timezone(self.zone)) + now = self._now.astimezone(pytz.timezone(self.valid_zone)) return { "hour": now.hour if now.hour <= 12 else now.hour % 12, "minute": now.minute, @@ -66,16 +83,21 @@ def time_info(self) -> dict[str, Any]: def on_load(self): """Switch the clock off when the page refreshes.""" self.running = False - self.zone = "US/Pacific" + self.refresh() + + def refresh(self): + """Refresh the clock.""" + self._now = datetime.now(timezone.utc) + @rx.background async def tick(self): """Update the clock every second.""" - # Sleep for a second. - await asyncio.sleep(1) + while self.running: + async with self: + self.refresh() - # If the clock is running, tick again. - if self.running: - return State.tick + # Sleep for a second. + await asyncio.sleep(1) def flip_switch(self, running: bool): """Start or stop the clock. @@ -161,7 +183,7 @@ def timezone_select() -> rx.Component: TIMEZONES, placeholder="Select a time zone.", on_change=State.set_zone, - value=State.zone, + value=State.valid_zone, bg="#white", ) diff --git a/clock/requirements.txt b/clock/requirements.txt index eac3e8f5..aa2fc63e 100644 --- a/clock/requirements.txt +++ b/clock/requirements.txt @@ -1,2 +1,2 @@ -reflex>=0.2.0 +reflex>=0.2.9 pytz==2022.7.1 \ No newline at end of file diff --git a/counter/requirements.txt b/counter/requirements.txt index bc6a7f2e..c8ade313 100644 --- a/counter/requirements.txt +++ b/counter/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 +reflex>=0.2.9 diff --git a/crm/requirements.txt b/crm/requirements.txt index 210218f4..825836d9 100644 --- a/crm/requirements.txt +++ b/crm/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 \ No newline at end of file +reflex>=0.2.9 \ No newline at end of file diff --git a/dalle/.gitignore b/dalle/.gitignore index bf56ff42..f6711845 100644 --- a/dalle/.gitignore +++ b/dalle/.gitignore @@ -1,3 +1,4 @@ +*.db *.py[cod] .web __pycache__/ diff --git a/dalle/dalle/dalle.py b/dalle/dalle/dalle.py index 3bc4dc15..2d715f89 100644 --- a/dalle/dalle/dalle.py +++ b/dalle/dalle/dalle.py @@ -3,14 +3,15 @@ import reflex as rx - class State(rx.State): """The app state.""" + image_url = "" image_processing = False image_made = False + def get_dalle_result(self, form_data: dict[str, str]): - prompt_text:str = form_data["prompt_text"] + prompt_text: str = form_data["prompt_text"] self.image_made = False self.image_processing = True yield @@ -24,6 +25,7 @@ def get_dalle_result(self, form_data: dict[str, str]): self.image_processing = False yield rx.window_alert("Error with OpenAI Execution.") + def index(): return rx.center( rx.vstack( @@ -60,7 +62,8 @@ def index(): background="radial-gradient(circle at 22% 11%,rgba(62, 180, 137,.20),hsla(0,0%,100%,0) 19%),radial-gradient(circle at 82% 25%,rgba(33,150,243,.18),hsla(0,0%,100%,0) 35%),radial-gradient(circle at 25% 61%,rgba(250, 128, 114, .28),hsla(0,0%,100%,0) 55%)", ) + # Add state and page to the app. app = rx.App(state=State) -app.add_page(index, title="Pynecone:DALL-E") +app.add_page(index, title="Reflex:DALL-E") app.compile() diff --git a/dalle/requirements.txt b/dalle/requirements.txt index 743a150e..63a0fc6d 100644 --- a/dalle/requirements.txt +++ b/dalle/requirements.txt @@ -1,2 +1,2 @@ -reflex>=0.2.0 +reflex>=0.2.9 openai diff --git a/ecommerce/.gitignore b/ecommerce/.gitignore index bf56ff42..f6711845 100644 --- a/ecommerce/.gitignore +++ b/ecommerce/.gitignore @@ -1,3 +1,4 @@ +*.db *.py[cod] .web __pycache__/ diff --git a/ecommerce/ecommerce/ecommerce.py b/ecommerce/ecommerce/ecommerce.py index 15443afb..0b0bb73a 100644 --- a/ecommerce/ecommerce/ecommerce.py +++ b/ecommerce/ecommerce/ecommerce.py @@ -127,7 +127,7 @@ def inventory(): data=State.product_data, pagination=True, sort=True, - search=True, + # search=True, ) return rx.vstack(search_bar, table) diff --git a/ecommerce/requirements.txt b/ecommerce/requirements.txt index 685db61e..cc43b3bf 100644 --- a/ecommerce/requirements.txt +++ b/ecommerce/requirements.txt @@ -1,3 +1,3 @@ -reflex>=0.2.0 +reflex>=0.2.9 icecream==2.1.1 pandas==2.1.0 \ No newline at end of file diff --git a/fragments/requirements.txt b/fragments/requirements.txt index 210218f4..825836d9 100644 --- a/fragments/requirements.txt +++ b/fragments/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 \ No newline at end of file +reflex>=0.2.9 \ No newline at end of file diff --git a/github-stats/github_stats/github_stats.py b/github-stats/github_stats/github_stats.py index a96e334b..c2e1aedb 100644 --- a/github-stats/github_stats/github_stats.py +++ b/github-stats/github_stats/github_stats.py @@ -143,18 +143,24 @@ def index() -> rx.Component: on_submit=State.handle_form, ), rx.box( - rx.bar_chart( - rx.graphing_tooltip(cursor=False), - rx.bar( + rx.recharts.bar_chart( + rx.recharts.graphing_tooltip(cursor=False), + rx.recharts.bar( data_key="repositoriesContributedTo", stroke="#8884d8", fill="#8884d8", ), - rx.bar(data_key="mergedPullRequests", stroke="#82ca9d", fill="#82ca9d"), - rx.bar(data_key="openIssues", stroke="#ffc658", fill="#ffc658"), - rx.bar(data_key="closedIssues", stroke="#ff0000", fill="#ff0000"), - rx.x_axis(data_key="login"), - rx.y_axis(), + rx.recharts.bar( + data_key="mergedPullRequests", stroke="#82ca9d", fill="#82ca9d" + ), + rx.recharts.bar( + data_key="openIssues", stroke="#ffc658", fill="#ffc658" + ), + rx.recharts.bar( + data_key="closedIssues", stroke="#ff0000", fill="#ff0000" + ), + rx.recharts.x_axis(data_key="login"), + rx.recharts.y_axis(), data=State.user_stats, ), width="100%", diff --git a/github-stats/requirements.txt b/github-stats/requirements.txt index 66a9f3f6..c8ade313 100644 --- a/github-stats/requirements.txt +++ b/github-stats/requirements.txt @@ -1 +1 @@ -reflex>=0.3.0a1 +reflex>=0.2.9 diff --git a/gpt/requirements.txt b/gpt/requirements.txt index 743a150e..63a0fc6d 100644 --- a/gpt/requirements.txt +++ b/gpt/requirements.txt @@ -1,2 +1,2 @@ -reflex>=0.2.0 +reflex>=0.2.9 openai diff --git a/local_auth/requirements.txt b/local_auth/requirements.txt index 86a89dac..be916f20 100644 --- a/local_auth/requirements.txt +++ b/local_auth/requirements.txt @@ -1,3 +1,3 @@ -reflex>=0.2.7 +reflex>=0.2.9 passlib bcrypt diff --git a/lorem-stream/lorem_stream/lorem_stream.py b/lorem-stream/lorem_stream/lorem_stream.py index f0e91e5c..2f413732 100644 --- a/lorem-stream/lorem_stream/lorem_stream.py +++ b/lorem-stream/lorem_stream/lorem_stream.py @@ -69,7 +69,9 @@ def render_task(task_id: int) -> rx.Component: is_indeterminate=LoremState.progress[task_id] < 1, ), rx.button( - rx.cond(LoremState.progress[task_id] < LoremState.end_at[task_id], "⏯️", "🔄"), + rx.cond( + LoremState.progress[task_id] < LoremState.end_at[task_id], "⏯️", "🔄" + ), on_click=LoremState.toggle_running(task_id), ), rx.button("❌", on_click=LoremState.kill(task_id)), diff --git a/lorem-stream/requirements.txt b/lorem-stream/requirements.txt index 5f7645ad..ca4f55a6 100644 --- a/lorem-stream/requirements.txt +++ b/lorem-stream/requirements.txt @@ -1,2 +1,2 @@ -reflex>=0.2.8 +reflex>=0.2.9 lorem_text>=2.1 diff --git a/nba/requirements.txt b/nba/requirements.txt index d6145b5c..0e808d41 100644 --- a/nba/requirements.txt +++ b/nba/requirements.txt @@ -1,4 +1,4 @@ -reflex>=0.2.0 +reflex>=0.2.9 plotly-express plotly pandas diff --git a/qr-scanner/qr_scanner/component.py b/qr-scanner/qr_scanner/component.py index c1f95e0e..04ca0546 100644 --- a/qr-scanner/qr_scanner/component.py +++ b/qr-scanner/qr_scanner/component.py @@ -20,12 +20,15 @@ class QrScanner(NoSSRComponent): container_style: rx.Var[Dict[str, str]] video_style: rx.Var[Dict[str, str]] - def get_controlled_triggers(self) -> Dict[str, Var]: + def get_event_triggers(self) -> Dict[str, Var]: """Dict mapping (event -> expected arguments).""" - return super().get_controlled_triggers() | { - "on_result": rx.EVENT_ARG, - "on_decode": rx.EVENT_ARG, - "on_error": BaseVar(name=rx.EVENT_ARG.name + "?.message", type_=str), + + return { + **super().get_event_triggers(), + "on_result": lambda e0: [e0], + "on_decode": lambda e0: [e0], + "on_error": lambda e0: [BaseVar(name="_e0?.message")], } -qr_scanner = QrScanner.create \ No newline at end of file + +qr_scanner = QrScanner.create diff --git a/qr-scanner/qr_scanner/qr_scanner.py b/qr-scanner/qr_scanner/qr_scanner.py index 165222dd..545e60f0 100644 --- a/qr-scanner/qr_scanner/qr_scanner.py +++ b/qr-scanner/qr_scanner/qr_scanner.py @@ -73,4 +73,4 @@ def index() -> rx.Component: app = rx.App() app.add_page(index) -app.compile() \ No newline at end of file +app.compile() diff --git a/qr-scanner/requirements.txt b/qr-scanner/requirements.txt index 4e22d85e..825836d9 100644 --- a/qr-scanner/requirements.txt +++ b/qr-scanner/requirements.txt @@ -1 +1 @@ -reflex>=0.2.5 \ No newline at end of file +reflex>=0.2.9 \ No newline at end of file diff --git a/qr-scanner/rxconfig.py b/qr-scanner/rxconfig.py index 3e6a082f..2957d6f3 100644 --- a/qr-scanner/rxconfig.py +++ b/qr-scanner/rxconfig.py @@ -1,9 +1,10 @@ import reflex as rx + class QrscannerConfig(rx.Config): pass + config = QrscannerConfig( app_name="qr_scanner", - frontend_packages=["@yudiel/react-qr-scanner"], ) diff --git a/quiz/.gitignore b/quiz/.gitignore index bf56ff42..f6711845 100644 --- a/quiz/.gitignore +++ b/quiz/.gitignore @@ -1,3 +1,4 @@ +*.db *.py[cod] .web __pycache__/ diff --git a/quiz/quiz/quiz.py b/quiz/quiz/quiz.py index edf9b55e..429db03e 100644 --- a/quiz/quiz/quiz.py +++ b/quiz/quiz/quiz.py @@ -16,10 +16,12 @@ class State(rx.State): """The app state.""" + default_answers = [None, None, [False, False, False, False, False]] - answers:List[Any] + answers: List[Any] answer_key = ["False", "[10, 20, 30, 40]", [False, False, True, True, True]] score: int + def onload(self): self.answers = copy.deepcopy(self.default_answers) @@ -38,6 +40,10 @@ def submit(self): self.score = int(correct / total * 100) return rx.redirect("/result") + @rx.var + def percent_score(self): + return f"{self.score}%" + def header(): return rx.vstack( @@ -63,7 +69,7 @@ def question1(): ["True", "False"], default_value=State.default_answers[0], default_checked=True, - on_change=lambda answer: State.set_answers(answer, 0) + on_change=lambda answer: State.set_answers(answer, 0), ), style=question_style, ) diff --git a/quiz/quiz/results.py b/quiz/quiz/results.py index 8a115a3b..98f999ab 100644 --- a/quiz/quiz/results.py +++ b/quiz/quiz/results.py @@ -33,7 +33,7 @@ def results(State): rx.divider(), rx.center( rx.circular_progress( - rx.circular_progress_label(State.score + "%"), + rx.circular_progress_label(State.percent_score), value=State.score, size="3em", ) @@ -49,7 +49,7 @@ def results(State): ), rx.foreach(State.answers, lambda answer, i: render_answer(State, i)), ), - rx.link(rx.button("Take Quiz Again"), href="/"), + rx.box(rx.link(rx.button("Take Quiz Again"), href="/")), bg="white", padding_x="5em", padding_y="2em", diff --git a/quiz/requirements.txt b/quiz/requirements.txt index bc6a7f2e..c8ade313 100644 --- a/quiz/requirements.txt +++ b/quiz/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 +reflex>=0.2.9 diff --git a/sales/.gitignore b/sales/.gitignore index bf56ff42..f6711845 100644 --- a/sales/.gitignore +++ b/sales/.gitignore @@ -1,3 +1,4 @@ +*.db *.py[cod] .web __pycache__/ diff --git a/sales/assets/favicon.ico b/sales/assets/favicon.ico index 8a93bfa7..609f6abc 100644 Binary files a/sales/assets/favicon.ico and b/sales/assets/favicon.ico differ diff --git a/sales/requirements.txt b/sales/requirements.txt index 743a150e..63a0fc6d 100644 --- a/sales/requirements.txt +++ b/sales/requirements.txt @@ -1,2 +1,2 @@ -reflex>=0.2.0 +reflex>=0.2.9 openai diff --git a/sales/sales/sales.py b/sales/sales/sales.py index 1cc03020..dec903f0 100644 --- a/sales/sales/sales.py +++ b/sales/sales/sales.py @@ -3,7 +3,45 @@ from .models import Customer -products = {'T-shirt': { 'description': 'A plain white t-shirt made of 100% cotton.', 'price': 10.99 }, 'Jeans': { 'description': 'A pair of blue denim jeans with a straight leg fit.', 'price': 24.99 }, 'Hoodie': { 'description': 'A black hoodie made of a cotton and polyester blend.', 'price': 34.99 }, 'Cardigan': { 'description': 'A grey cardigan with a V-neck and long sleeves.', 'price': 36.99 }, 'Joggers': { 'description': 'A pair of black joggers made of a cotton and polyester blend.', 'price': 44.99 }, 'Dress': { 'description': 'A black dress made of 100% polyester.', 'price': 49.99 }, 'Jacket': { 'description': 'A navy blue jacket made of 100% cotton.', 'price': 55.99 }, 'Skirt': { 'description': 'A brown skirt made of a cotton and polyester blend.', 'price': 29.99 }, 'Shorts': { 'description': 'A pair of black shorts made of a cotton and polyester blend.', 'price': 19.99 }, 'Sweater': { 'description': 'A white sweater with a crew neck and long sleeves.', 'price': 39.99}} +products = { + "T-shirt": { + "description": "A plain white t-shirt made of 100% cotton.", + "price": 10.99, + }, + "Jeans": { + "description": "A pair of blue denim jeans with a straight leg fit.", + "price": 24.99, + }, + "Hoodie": { + "description": "A black hoodie made of a cotton and polyester blend.", + "price": 34.99, + }, + "Cardigan": { + "description": "A grey cardigan with a V-neck and long sleeves.", + "price": 36.99, + }, + "Joggers": { + "description": "A pair of black joggers made of a cotton and polyester blend.", + "price": 44.99, + }, + "Dress": {"description": "A black dress made of 100% polyester.", "price": 49.99}, + "Jacket": { + "description": "A navy blue jacket made of 100% cotton.", + "price": 55.99, + }, + "Skirt": { + "description": "A brown skirt made of a cotton and polyester blend.", + "price": 29.99, + }, + "Shorts": { + "description": "A pair of black shorts made of a cotton and polyester blend.", + "price": 19.99, + }, + "Sweater": { + "description": "A white sweater with a crew neck and long sleeves.", + "price": 39.99, + }, +} class State(rx.State): @@ -43,7 +81,7 @@ def add_customer(self): def customer_page(self): """The customer page.""" return rx.redirect("/") - + def onboarding_page(self): """The onboarding page.""" return rx.redirect("/onboarding") @@ -54,15 +92,16 @@ def delete_customer(self, email: str): session.query(Customer).filter_by(email=email).delete() session.commit() - generate_email_data:dict={} + generate_email_data: dict = {} + async def call_openai(self): - name:str = self.generate_email_data["name"] - email:str = self.generate_email_data["email"] - age:int = self.generate_email_data["age"] - gender:str = self.generate_email_data["gender"] - location:str = self.generate_email_data["location"] - job:str = self.generate_email_data["job"] - salary:int = self.generate_email_data["salary"] + name: str = self.generate_email_data["name"] + email: str = self.generate_email_data["email"] + age: int = self.generate_email_data["age"] + gender: str = self.generate_email_data["gender"] + location: str = self.generate_email_data["location"] + job: str = self.generate_email_data["job"] + salary: int = self.generate_email_data["salary"] response = openai.Completion.create( model="text-davinci-003", prompt=f"Based on these {products} write a sales email to {name} adn email {email} who is {age} years old and a {gender} gender. {name} lives in {location} and works as a {job} and earns {salary} per year. Make sure the email reccomends one product only and is personalized to {name}. The company is named Reflex its website is https://reflex.dev", @@ -73,8 +112,10 @@ async def call_openai(self): presence_penalty=0, ) self.gen_response = False - self.email_content_data = response.choices[0].text # save the data related to email_content - return rx.set_value("email_content", self.email_content_data) # update layout of email_content manually + # save the data related to email_content + self.email_content_data = response.choices[0].text + # update layout of email_content manually + return rx.set_value("email_content", self.email_content_data) def generate_email( self, @@ -97,31 +138,30 @@ def generate_email( self.gen_response = True return State.call_openai - - @rx.var def get_users(self) -> list[Customer]: """Get all users from the database.""" with rx.session() as session: self.users = session.query(Customer).all() return self.users + def open_text_area(self): self.text_area_disabled = False + def close_text_area(self): self.text_area_disabled = True - def navbar(): """The navbar for the top of the page.""" return rx.box( rx.hstack( rx.link( rx.hstack( - rx.image(src="favicon.ico", width="50px"), + rx.image(src="/favicon.ico", width="50px"), rx.heading("Reflex | Personalized Sales", size="lg"), ), - href="https://reflex.dev", + href="/", ), rx.menu( rx.menu_button( @@ -130,9 +170,7 @@ def navbar(): rx.menu_list( rx.link( rx.menu_item( - rx.hstack( - rx.text("Customers"), rx.icon(tag="hamburger") - ) + rx.hstack(rx.text("Customers"), rx.icon(tag="hamburger")) ), href="/", ), @@ -186,9 +224,8 @@ def show_customer(user: Customer): user.gender, user.location, user.job, - user.salary, + user.salary, ), - bg="blue", color="white", ) @@ -243,7 +280,12 @@ def index(): rx.vstack( rx.hstack( rx.heading("Customers"), - rx.button(rx.icon(tag="add"), on_click=State.onboarding_page, bg="#F7FAFC", border="1px solid #ddd"), + rx.button( + rx.icon(tag="add"), + on_click=State.onboarding_page, + bg="#F7FAFC", + border="1px solid #ddd", + ), ), rx.table_container( rx.table( diff --git a/snakegame/requirements.txt b/snakegame/requirements.txt index 210218f4..825836d9 100644 --- a/snakegame/requirements.txt +++ b/snakegame/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 \ No newline at end of file +reflex>=0.2.9 \ No newline at end of file diff --git a/snakegame/snakegame/snakegame.py b/snakegame/snakegame/snakegame.py index e23df403..b5e4db0e 100644 --- a/snakegame/snakegame/snakegame.py +++ b/snakegame/snakegame/snakegame.py @@ -1,149 +1,326 @@ -import reflex as rx import asyncio import random +from typing import Any, Dict, List + +import reflex as rx + +N = 19 # There is a N*N grid for ground of snake +COLOR_NONE = "#EEEEEE" +COLOR_BODY = "#008800" +COLOR_FOOD = "#FF00FF" +COLOR_DEAD = "#FF0000" +# Tuples representing the directions the snake head can move +HEAD_U = (0, -1) +HEAD_D = (0, 1) +HEAD_L = (-1, 0) +HEAD_R = (1, 0) +INITIAL_SNAKE = [ # all (X,Y) for snake's body + (-1, -1), + (-1, -1), + (-1, -1), + (-1, -1), + (-1, -1), + (10, 15), # Starting head position +] +INITIAL_FOOD = (5, 5) # X, Y of food + + +def get_new_head(old_head: tuple[int, int], dir: tuple[int, int]) -> tuple[int, int]: + """Calculate the new head position based on the given direction.""" + x, y = old_head + return (x + dir[0] + N) % N, (y + dir[1] + N) % N + + +def to_cell_index(x: int, y: int) -> int: + """Calculate the index into the game board for the given (X, Y).""" + return x + N * y -N = 19 # There is a N*N grid for ground of snake -COLOR_NONE="#EEEEEE" -COLOR_BODY="#008800" -COLOR_FOOD="#FF00FF" -HEAD_U = "U" -HEAD_D = "D" -HEAD_L = "L" -HEAD_R = "R" class State(rx.State): - tmpdir:str = HEAD_R - dir:str = HEAD_R # direction of what head of snake face - snake:list[list[int]] = [[10,10], [10,11],[10,12],[10,13],[10,14],[10,15]] # all (X,Y) for snake's body - food:list[int] = [5,5] # X, Y of food - cells:list[str] = (N*N)*[COLOR_NONE] - tick_cnt:int = 1 - score: int = 0 - magic:int = 1 - rate:int = 10 - start: bool = False - def turnOnTick(self): - self.start = True - if self.start: - #print(self.snake) - #print(self.food) - return State.tick - - def turnOffTick(self): - self.start = False - if self.start: - return State.tick + dir: str = HEAD_R # Direction the snake head is facing currently + moves: list[tuple[int, int]] = [] # Queue of moves based on user input + snake: list[tuple[int, int]] = INITIAL_SNAKE # Body of snake + food: tuple[int, int] = INITIAL_FOOD # X, Y location of food + cells: list[str] = (N * N) * [COLOR_NONE] # The game board to be rendered + score: int = 0 # Player score + magic: int = 1 # Number of points per food eaten + rate: int = 10 # 5 divide by rate determines tick period + died: bool = False # If the snake is dead (game over) + tick_cnt: int = 1 # How long the game has been running + running: bool = False + _n_tasks: int = 0 + + def play(self): + """Start / resume the game.""" + if not self.running: + if self.died: + # If the player is dead, reset game state before beginning. + self.reset() + self.running = True + return State.loop + + def pause(self): + """Signal the game to pause.""" + self.running = False + def flip_switch(self, start): - self.start = start - if self.start: - return State.tick - - async def tick(self): - if self.start: - await asyncio.sleep(0.5) - self.dir = self.tmpdir - head = self.snake[-1].copy() - if(self.dir==HEAD_U): - head[1] += (N-1) - head[1] %= N - elif(self.dir==HEAD_D): - head[1] += (N+1) - head[1] %= N - elif(self.dir==HEAD_L): - head[0] += (N-1) - head[0] %= N - elif(self.dir==HEAD_R): - head[0] += (N+1) - head[0] %= N - if(head in self.snake): - self.start = False - self.magic = 1 - for i in range(N*N): - self.cells[i] = COLOR_NONE - self.snake = [[10,10], [10,11],[10,12],[10,13],[10,14],[10,15]].copy() - self.food = [5,5] - self.dir = HEAD_R - await asyncio.sleep(3) - return State.tick - - self.cells[head[0]+ N*head[1]] = COLOR_BODY - self.snake.append(head.copy()) - FOOD_EATEN = False - while(self.food in self.snake): - FOOD_EATEN = True - self.food = [random.randint(0,N-1), random.randint(0,N-1)] - self.cells[self.food[0]+ N*self.food[1]] = COLOR_FOOD - if(FOOD_EATEN==False): - self.cells[self.snake[0][0]+ N*self.snake[0][1]] = COLOR_NONE - del self.snake[0] - else: - self.score+=self.magic - self.magic += 1 - #self.rate = (int)(100*((float)self.score / (float)self.tick_cnt)) - self.rate = (int)(100*((float)(self.score)/(float)(self.tick_cnt))) - self.tick_cnt += 1 - #print(self.tick_cnt) - - return State.tick + """Toggle whether the game is running or paused.""" + if start: + return State.play + else: + return State.pause + + def _next_move(self): + """Returns the next direction the snake head should move in.""" + return self.moves[0] if self.moves else self.dir + + def _last_move(self): + """Returns the last queued direction the snake head should move in.""" + return self.moves[-1] if self.moves else self.dir + + @rx.background + async def loop(self): + """The main game loop, implemented as a singleton background task. + + Responsible for updating the game state on each tick. + """ + async with self: + if self._n_tasks > 0: + # Only start one loop task at a time. + return + self._n_tasks += 1 + + while self.running: + # Sleep based on the current rate + await asyncio.sleep(5 / self.rate) + async with self: + # Which direction will the snake move? + self.dir = self._next_move() + if self.moves: + # Remove the processed next move from the queue + del self.moves[0] + + # Calculate new head position + head = get_new_head(self.snake[-1], dir=self.dir) + if head in self.snake: + # New head position crashes into snake body, Game Over + print(head, self.snake) + self.running = False + self.died = True + self.cells[to_cell_index(*head)] = COLOR_DEAD + break + + # Move the snake + self.snake.append(head) + self.cells[to_cell_index(*head)] = COLOR_BODY + food_eaten = False + while self.food in self.snake: + food_eaten = True + self.food = (random.randint(0, N - 1), random.randint(0, N - 1)) + self.cells[to_cell_index(*self.food)] = COLOR_FOOD + if not food_eaten: + # Advance the snake + self.cells[to_cell_index(*self.snake[0])] = COLOR_NONE + del self.snake[0] + else: + # Grow the snake (and the score) + self.score += self.magic + self.magic += 1 + self.rate = 10 + self.magic + self.tick_cnt += 1 + + async with self: + # Decrement task counter, since we're about to return + self._n_tasks -= 1 def arrow_up(self): - if(self.dir != HEAD_D): - self.tmpdir = HEAD_U - return + """Queue a move up.""" + if self._last_move() != HEAD_D: + self.moves.append(HEAD_U) + def arrow_left(self): - if(self.dir != HEAD_R): - self.tmpdir = HEAD_L - return + """Queue a move left.""" + if self._last_move() != HEAD_R: + self.moves.append(HEAD_L) + def arrow_right(self): - if(self.dir != HEAD_L): - self.tmpdir = HEAD_R - return + """Queue a move right.""" + if self._last_move() != HEAD_L: + self.moves.append(HEAD_R) + def arrow_down(self): - if(self.dir != HEAD_U): - self.tmpdir = HEAD_D - return - def arrow_none(self): - return + """Queue a move down.""" + if self._last_move() != HEAD_U: + self.moves.append(HEAD_D) + + def arrow_rel_left(self): + """Queue a move left relative to the current direction.""" + last_move = self._last_move() + if last_move == HEAD_U: + self.arrow_left() + elif last_move == HEAD_L: + self.arrow_down() + elif last_move == HEAD_D: + self.arrow_right() + elif last_move == HEAD_R: + self.arrow_up() + + def arrow_rel_right(self): + """Queue a move right relative to the current direction.""" + last_move = self._last_move() + if last_move == HEAD_U: + self.arrow_right() + elif last_move == HEAD_L: + self.arrow_up() + elif last_move == HEAD_D: + self.arrow_left() + elif last_move == HEAD_R: + self.arrow_down() + + def handle_key(self, key): + """Handle keyboard press.""" + { + "ArrowUp": self.arrow_up, + "ArrowLeft": self.arrow_left, + "ArrowRight": self.arrow_right, + "ArrowDown": self.arrow_down, + ",": self.arrow_rel_left, + ".": self.arrow_rel_right, + }[key]() + + +class GlobalKeyWatcher(rx.Fragment): + """A component that attaches a keydown handler to the document. + + The handler only calls the backend function if the pressed key is one of the + specified keys. + + Requires custom javascript to support this functionality at the moment. + """ + + # List of keys to trigger on + keys: rx.vars.Var[List[str]] = [] + + def _get_hooks(self) -> str | None: + return """ +useEffect(() => { + const handle_key = (_e0) => { + if (%s.includes(_e0.key)) + %s + } + document.addEventListener("keydown", handle_key, false); + return () => { + document.removeEventListener("keydown", handle_key, false); + } +}) +""" % ( + self.keys, + rx.utils.format.format_event_chain(self.event_triggers["on_key_down"]), + ) + + def get_event_triggers(self) -> Dict[str, Any]: + return { + "on_key_down": lambda e0: [e0.key], + } + + def render(self) -> str: + # This component has no visual element. + return "" + def colored_box(color, index): + """One square of the game grid.""" return rx.box(bg=color, width="1em", height="1em", border="1px solid white") -def index(): + +def stat_box(label, value): + """One of the score, magic, or rate boxes.""" return rx.vstack( - rx.hstack( - rx.button("PAUSE", on_click=State.turnOffTick, color_scheme="blue", border_radius="1em"), - rx.button("RUN", on_click=State.turnOnTick, color_scheme="green", border_radius="1em"), - rx.switch(is_checked=State.start, on_change=State.flip_switch), - ), - - rx.hstack( - rx.vstack( - rx.heading("RATE", font_size="1em"), - rx.heading(State.rate, font_size="2em"), - bg_color="yellow", - border_width="1px", - padding_left="1em", - padding_right="1em", + rx.heading(label, font_size="1em"), + rx.heading(value, font_size="2em"), + bg_color="yellow", + border_width="1px", + padding_left="1em", + padding_right="1em", + ) + + +def control_button(label, on_click): + """One of the arrow buttons for touch/mouse control.""" + return rx.button( + label, + on_click=on_click, + color_scheme="red", + border_radius="1em", + font_size="2em", + ) + +def padding_button(): + """A button that is used for padding in the controls panel.""" + return rx.button( + "○", + color_scheme="#FFFFFF", + border_radius="1em", + font_size="2em", + ) + + +def controls_panel(): + """The controls panel of arrow buttons.""" + return rx.hstack( + GlobalKeyWatcher.create( + keys=["ArrowUp", "ArrowLeft", "ArrowRight", "ArrowDown", ",", "."], + on_key_down=State.handle_key, + ), + rx.vstack( + padding_button(), + control_button( + "←", + on_click=State.arrow_left, + ), + ), + rx.vstack( + control_button( + "↑", + on_click=State.arrow_up, + ), + control_button( + "↓", + on_click=State.arrow_down, ), - - rx.vstack( - rx.heading("SCORE", font_size="1em"), - rx.heading(State.score, font_size="2em"), - bg_color="yellow", - border_width="1px", - padding_left="1em", - padding_right="1em", + ), + rx.vstack( + padding_button(), + control_button( + "→", + on_click=State.arrow_right, + ), + ), + ) + +def index(): + return rx.vstack( + rx.hstack( + rx.button( + "PAUSE", + on_click=State.pause, + color_scheme="blue", + border_radius="1em", ), - rx.vstack( - rx.heading("MAGIC", font_size="1em"), - rx.heading(State.magic, font_size="2em"), - bg_color="yellow", - border_width="1px", - padding_left="1em", - padding_right="1em", + rx.button( + "RUN", + on_click=State.play, + color_scheme="green", + border_radius="1em", ), + rx.switch(is_checked=State.running, on_change=State.flip_switch), + ), + rx.hstack( + stat_box("RATE", State.rate), + stat_box("SCORE", State.score), + stat_box("MAGIC", State.magic), ), # Usage of foreach, please refer https://reflex.app/docs/library/layout/foreach rx.responsive_grid( @@ -153,23 +330,12 @@ def index(): ), columns=[N], ), - rx.hstack( - rx.vstack( - rx.button("○", on_click=State.arrow_none, color_scheme="#FFFFFFFF", border_radius="1em",font_size="2em"), - rx.button("←", on_click=State.arrow_left, color_scheme="red", border_radius="1em",font_size="2em"), - ), - rx.vstack( - rx.button("↑", on_click=State.arrow_up, color_scheme="red", border_radius="1em",font_size="2em"), - rx.button("↓", on_click=State.arrow_down, color_scheme="red", border_radius="1em",font_size="2em"), - ), - rx.vstack( - rx.button("○", on_click=State.arrow_none, color_scheme="#FFFFFFFF", border_radius="1em",font_size="2em"), - rx.button("→", on_click=State.arrow_right, color_scheme="red", border_radius="1em",font_size="2em"), - ), - ), + rx.cond(State.died, rx.heading("Game Over 🐍")), + controls_panel(), padding_top="3%", ) + app = rx.App(state=State) app.add_page(index, title="snake game") diff --git a/tailwind/requirements.txt b/tailwind/requirements.txt index bc6a7f2e..c8ade313 100644 --- a/tailwind/requirements.txt +++ b/tailwind/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 +reflex>=0.2.9 diff --git a/todo/requirements.txt b/todo/requirements.txt index bc6a7f2e..c8ade313 100644 --- a/todo/requirements.txt +++ b/todo/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 +reflex>=0.2.9 diff --git a/translator/requirements.txt b/translator/requirements.txt index cea8d7e2..ec450bbd 100644 --- a/translator/requirements.txt +++ b/translator/requirements.txt @@ -1,3 +1,3 @@ -reflex>=0.2.0 +reflex>=0.2.9 googletrans-py==4.0.0 requests>=2.28.1 diff --git a/traversal/requirements.txt b/traversal/requirements.txt index 210218f4..825836d9 100644 --- a/traversal/requirements.txt +++ b/traversal/requirements.txt @@ -1 +1 @@ -reflex>=0.2.0 \ No newline at end of file +reflex>=0.2.9 \ No newline at end of file diff --git a/traversal/traversal/traversal.py b/traversal/traversal/traversal.py index 06881bd1..870c6dc5 100644 --- a/traversal/traversal/traversal.py +++ b/traversal/traversal/traversal.py @@ -52,10 +52,9 @@ def new_graph(self): def run(self): """Run the selected algorithm.""" if self.option == "DFS": - return self.run_dfs + return State.run_dfs elif self.option == "BFS": - return self.run_bfs - + return State.run_bfs async def run_dfs(self): """DFS algorithm on a 1d array.""" @@ -93,7 +92,7 @@ async def run_dfs(self): and colors[i2][j2] != "blue" ): self.s.append((i2, j2)) - return self.run_dfs + return State.run_dfs return rx.window_alert("No path found") @@ -140,7 +139,7 @@ async def run_bfs(self): ): q.append((i2, j2)) self.q.append((i2, j2)) - return self.run_bfs + return State.run_bfs return rx.window_alert("No path found")