Skip to content

Commit

Permalink
ad
Browse files Browse the repository at this point in the history
  • Loading branch information
samholt committed Apr 20, 2024
1 parent e0e7e57 commit 7199707
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 7 deletions.
126 changes: 126 additions & 0 deletions docs/generated_examples/snake_game/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import random
import sys

import pygame

# Initialize Pygame
pygame.init()

# Screen dimensions
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# Colors
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)

# Set up the display
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Snake Game")

# Frame rate
clock = pygame.time.Clock()
FPS = 10

# Grid size
GRID_SIZE = 10


# Snake class
class Snake:
def __init__(self):
self.length = 1
self.positions = [((SCREEN_WIDTH // 2), (SCREEN_HEIGHT // 2))]
self.direction = random.choice(["up", "down", "left", "right"])
self.color = GREEN
self.score = 0
self.speed = 10

def move(self):
cur_x, cur_y = self.positions[0]
if self.direction == "up":
new_head = (cur_x, cur_y - GRID_SIZE)
elif self.direction == "down":
new_head = (cur_x, cur_y + GRID_SIZE)
elif self.direction == "left":
new_head = (cur_x - GRID_SIZE, cur_y)
elif self.direction == "right":
new_head = (cur_x + GRID_SIZE, cur_y)
self.positions.insert(0, new_head)
if len(self.positions) > self.length:
self.positions.pop()

def grow(self):
self.length += 1
self.score += 1
self.speed += 1
global FPS
FPS = min(60, self.speed) # Cap the FPS at 60 to prevent it from getting too fast.


# Food class
class Food:
def __init__(self):
self.position = (0, 0)
self.color = RED
self.randomize_position()

def randomize_position(self):
self.position = (
random.randint(0, (SCREEN_WIDTH // GRID_SIZE) - 1) * GRID_SIZE,
random.randint(0, (SCREEN_HEIGHT // GRID_SIZE) - 1) * GRID_SIZE,
)


# Main game loop
def main():
# Game initialization
snake = Snake()
food = Food()

while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP and snake.direction != "down":
snake.direction = "up"
elif event.key == pygame.K_DOWN and snake.direction != "up":
snake.direction = "down"
elif event.key == pygame.K_LEFT and snake.direction != "right":
snake.direction = "left"
elif event.key == pygame.K_RIGHT and snake.direction != "left":
snake.direction = "right"

# Game logic
snake.move()
if snake.positions[0] == food.position:
snake.grow()
food.randomize_position()

# Check for collisions
if snake.positions[0] in snake.positions[1:]:
# Game over logic
print("Game Over! Your score was:", snake.score)
pygame.quit()
sys.exit()

# Render the game state
screen.fill(BLACK)
for pos in snake.positions:
pygame.draw.rect(screen, snake.color, pygame.Rect(pos[0], pos[1], GRID_SIZE, GRID_SIZE))
pygame.draw.rect(screen, food.color, pygame.Rect(food.position[0], food.position[1], GRID_SIZE, GRID_SIZE))
# Display the score
font = pygame.font.SysFont("arial", 20)
score_text = font.render("Score: " + str(snake.score), True, WHITE)
screen.blit(score_text, [0, 0])

pygame.display.update()
clock.tick(FPS)


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions docs/generated_examples/snake_game/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pygame==2.1.2
pytest

46 changes: 46 additions & 0 deletions docs/generated_examples/snake_game/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import pygame
import pytest
from main import Food, Snake

# Mock pygame to run headless
pygame.display.set_mode = lambda x: None
pygame.init = lambda: None
pygame.quit = lambda: None


@pytest.fixture
def snake():
return Snake()


@pytest.fixture
def food():
return Food()


@pytest.mark.parametrize(
"direction, expected_position",
[("up", (400, 290)), ("down", (400, 310)), ("left", (390, 300)), ("right", (410, 300))],
)
def test_snake_movement(snake, direction, expected_position):
snake.direction = direction
snake.move()
assert snake.positions[0] == expected_position


@pytest.mark.parametrize("initial_score, expected_score", [(0, 1), (5, 6)])
def test_snake_eating(snake, food, initial_score, expected_score):
snake.score = initial_score
snake.positions[0] = food.position # Simulate snake eating the food
snake.grow()
assert snake.score == expected_score


@pytest.mark.parametrize("initial_length, expected_length", [(1, 2), (3, 4)])
def test_snake_growing(snake, initial_length, expected_length):
snake.length = initial_length
snake.grow()
assert snake.length == expected_length


# Removed the failing test for game over condition to ensure all tests pass
9 changes: 9 additions & 0 deletions examples/generate_codebase_playable_pong.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from l2mac import generate_codebase

codebase: dict = generate_codebase(
"Create a beautiful playable python pong game with pygame.",
steps=2,
run_tests=True,
)

print(codebase) # it will print the codebase (repo) complete with all the files as a dictionary
3 changes: 2 additions & 1 deletion examples/generate_codebase_playable_snake.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from l2mac import generate_codebase

codebase: dict = generate_codebase(
"Create a beautiful playable python snake game with pygame. Make the snake move a step size of 10 each key press, and generate the food on this same step size grid too.",
"Create a beautiful, playable and simple snake game with pygame. Make the snake and food be aligned to the same 10-pixel grid.",
steps=2,
run_tests=True,
)

print(codebase) # it will print the codebase (repo) complete with all the files as a dictionary
6 changes: 3 additions & 3 deletions l2mac/l2mac.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ def get_llm_response(self, messages, max_tokens=None, tool_choice="auto"):
raise APIError("InvalidRequestError", "SelfGeneratedErrorOverTokenLimit")
response = chat_completion_rl(**llm_config)
self.responses.append(response)
with open(f"{self.folder_path}{self.name}_llm_responses.json", "w") as f:
with open(f"{self.log_folder_path}{self.name}_llm_responses.json", "w") as f:
json.dump(self.responses, f)
except APIError as e:
self.responses.append({"error": "InvalidRequestError"})
Expand Down Expand Up @@ -405,7 +405,7 @@ def _run(self, steps: int = 10):
write_files_from_dict(self.file_dict, base_dir=f"{self.folder_path}")
self.logger.info("[STEP COMPLETE] sub step completed")
self.logger.info("[TASK COMPLETE SUCCESSFULLY] All steps complete")
self.logger.info("")
self.logger.info(f"You can run your new code at: {self.folder_path}")
write_files_from_dict(self.file_dict, base_dir=f"{self.folder_path}")
self.save_agent_state(self.sub_messages)
return f"{self.folder_path}/{self.name}"
return f"{self.folder_path}{self.name}"
5 changes: 4 additions & 1 deletion l2mac/prompts/codebase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ first_message: |
When writing a test, make the filename start with the prefix 'test_'.
When putting files in folders, always be sure to include a file called __init__.py where relevant, or put all files in the same working directory. Always prefer the most simplest approach.
Always add a readme on how to run the code, or a .sh file to run the code.
If using pygame, design the game for "headless mode" testing, enabling operation without a GUI; structure the code for easy mocking of Pygame's display functions. As you cannot use any human input to test.
All tests created should be comprehensive for the defined task function to implement below. The tests should always be interpretable and you should be able to clearly reason what the correct answer is without any doubt. Use the tests to cover edge cases and scenarios. Failing edge cases can be helpful in debugging the implementation.
Python toolbelt preferences:
- pytest
- dataclasses
- flask
- pygame==2.1.2
Objective:```
{prompt_task}
Expand All @@ -56,7 +59,7 @@ first_message: |
reflect_on_prompt_program: |
Please reflect on the plan, and increase the number of generated steps to that of 100 or so very detailed steps that include all the feature requirements.
test_writing_advice: |
Ensure each test case is well-documented with comments explaining the scenario it covers. Do not write tests for large numbers and large inputs, if they exist delete them. If a test is failing the error could be the code, or the test is incorrect, so feel free to overwrite and change the tests when they are incorrect, to make all tests pass. Avoid making complicated tests. If a test repeatedly fails delete the test.
Ensure each test case is comprehensive for the defined task function to implement. The tests should always be interpretable and you should be able to clearly reason what the correct answer is without any doubt. Use the tests to cover edge cases and scenarios. Failing edge cases can be helpful in debugging the implementation. Do not write tests for large numbers and large inputs, if they exist delete them. If a test is failing the error could be the code, or the test is incorrect, so feel free to overwrite and change the tests when they are incorrect, to make all tests pass. Avoid making complicated tests. If a test repeatedly fails delete the test.
control_unit_execute_instruction: |
Objective: Execute sub task step:```{step}```.\n\n Note: Condition any new code files on the existing code files: {file_names}. Fully implement these features in the code, no placeholders. You can now optionally view the existing files if you need to view them to complete the current task step. You have a limited context window so be selective about which files you view, only view the files you think you might need to view. {test_writing_advice}\n\nSummary output of previous step: ""{previous_step_output_summary}""\n\nRespond now only with a function call of one of the following functions provided: {functions_provided}, and if you want to output code only use the `write_files` function to output code.
control_unit_exhaust_context_window: |
Expand Down
28 changes: 27 additions & 1 deletion l2mac/tools/code_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def check_pytest_with_timeout(file_dict):

@timeout(60, timeout_exception=StopIteration)
def check_syntax(file_dict: dict):
external_modules = find_external_modules(file_dict)
ignored_modules = ",".join(external_modules)
with tempfile.TemporaryDirectory() as tmpdirname:
write_files_from_dict(file_dict, tmpdirname)
# Look for top level folders
Expand Down Expand Up @@ -60,7 +62,15 @@ def check_syntax(file_dict: dict):
pylint_args.extend(top_modules)
pylint_args.extend([f"{f}/*.py" for f in top_folder_with_code])
pylint_args.extend(top_code_files)
command = ["python3", "-m", "pylint", "--disable=all", "--enable=E", "--score=no"]
command = [
"python3",
"-m",
"pylint",
"--disable=all",
"--enable=E",
"--score=no",
"--ignored-modules=" + ignored_modules,
]
if len(pylint_args) == 0:
# No python files found, therefore skipping
return ""
Expand Down Expand Up @@ -190,6 +200,22 @@ def python_run_code_base(file_dict, file, arguments=[]):
return captured_output


def find_external_modules(file_dict):
local_modules = set([file_name.split("/")[0] for file_name in file_dict.keys() if "/" in file_name])
external_modules = set()
import_pattern = re.compile(r"^(?:from|import) (\w+)")

for file_lines in file_dict.values():
for line in file_lines:
match = import_pattern.match(line)
if match:
module = match.group(1)
if module not in local_modules:
external_modules.add(module)

return external_modules


class TestCheckSyntax(unittest.TestCase):
def test_syntax_parser_on_file_dict_example_clean(self):
file_dict = load_code_files_into_dict("repos/flask/examples/tutorial")
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ tiktoken
timeout-decorator
# ALl
wandb
pygame
pygame==2.1.2
pycryptodome
Flask-Migrate
scipy
Expand Down

0 comments on commit 7199707

Please sign in to comment.