I had some time, so I thought I would take a look at Aider Chat↗, Aidir Chat is described by its authors as;
aider is a command-line chat tool that allows you to write and edit code with OpenAI’s GPT models. You can ask GPT to help you start a new project, or modify code in your existing git repo. Aider makes it easy to git commit, diff & undo changes proposed by GPT without copy/pasting. It also has features that help GPT-4 understand and modify larger codebases↗.
Info
As you may know from reading other blog posts, I am a macOS user, so the commands in this post will cover macOS only.
Installing Aider Chat is a simple process which involves running two commands, the first installs Aider Chat;
Before running Aider Chat, you need to provide a valid OpenAI API key. To do this, you can export it as an environment variable by running the following;
This will be valid for the life of your terminal session, or you can add it to a config file which is stored at ~/.aider.conf.yml by running the command which follows;
Now we have everything installed and configured lets dive straight in and create a project, to do this create a new folder and initiate a chat by running the commands below;
Creating the ~/.aider.conf.yml file
mkdir aider-blog-post
aider ./
This will show you something like the following output;
Creating the ~/.aider.conf.yml file
No git repo found, createonetotrackGPT'schanges(recommended)?yGit repository created in the current working directory.
Model: gpt-4
Git repo: .git
Repo-map: universal-ctags using 1024 tokens
Use /help to see in-chat commands.
From here, we can start asking questions;
Russ
Write a basic Snake game using Python and Pygame, it should have the following …
Have splash screen
Use the Arrow keys to control the snake
Each time the snake eats an apple, it should get faster
Use Emjois for the graphics where possible
Call the game file main.py and create a requirements.txt containing the requirements needed
This is what Aider had to say (please note, I have removed the code blocks for readability);
Aider
Sure, I can help you with that. Here’s a basic structure of how the game could be implemented.
First, let’s create the requirements.txt file
Now, let’s create the main.py file
Please note that this is a basic implementation of the game. You may need to add more features or modify the existing ones to suit your needs. Also, the use of emojis in Pygame is not straightforward and may require additional libraries or a different approach.
Allow creation of new file new_file/requirements.txt? y Add new_file/requirements.txt to git? y Allow creation of new file new_file/main.py? y Add new_file/main.py to git? y Applied edit to new_file/main.py Applied edit to new_file/requirements.txt Commit e7ef909 aider: Imported the necessary libraries and defined the main game loop, start screen, game over screen, and helper functions.
I then tried to run the code, and got an error - lets give Aider the error;
Russ
name ‘DARKGREEN’ is not defined
To which I got the following response;
Aider
It seems like the color DARKGREEN is not defined in your code. Let’s add it to the color definitions in your main.py file.
This will define DARKGREEN as a darker shade of green. You can adjust the RGB values as needed to get the exact color you want.
Applied edit to new_file/main.py Commit 06f2242 aider: Added DARKGREEN as a darker shade of green to the color definitions in main.py.
There were some other complaints about the code, after pasting the code in I decided to ask it something else;
Russ
More errors about colors, can you read through the code and name sure they are all correctly defined, the last error was “NameError: name ‘DARKGRAY’ is not defined. Did you mean: ‘DARKGREEN’?”
This did the trick, not only did Aider address the issue, but also checked the entire code for other missing elements;
Aider
Sure, I can help with that. It seems like the color DARKGRAY is not defined in your code. Let’s add it to the color definitions in your main.py file.
This will define DARKGRAY as a dark shade of gray. You can adjust the RGB values as needed to get the exact color you want.
Also, I noticed that there are some other variables like FPS and SNAKESPEED that are not defined in your code. Let’s add them as well.
This will define FPS and SNAKESPEED as 15. You can adjust these values as needed to get the desired game speed.
Applied edit to new_file/main.py Commit 00f2451 aider: Added color DARKGRAY and constants FPS and SNAKESPEED to main.py.
There was some more errors, this time around missing functions and some of the game logic - after about 10 minutes back and forth with Aider, I had a working game.
Russ
Check the new_file/main.py file for any other missing functions
Again, Aider was able to help;
Aider
Sure, I can help with that. Looking at your code, it seems like the functions drawApple and drawScore are being called but they are not defined in your code.
Let’s add basic definitions for these functions in your main.py file.
The drawApple function draws the apple on the game screen by drawing a red rectangle at the apple’s coordinates. The drawScore function displays the current score on the game screen. You can adjust the color and the size of the apple and the score display as needed.
Applied edit to new_file/main.py Commit 9d50b5a aider: Added missing functions drawApple and drawScore to main.py.
This time everything worked as expected and we have a mostly functional game as you can see from the following screens;
The game code is below, it is untouched from what Aider Chat created and as you can see, functionality wise is a little hit and miss, but it well commented and easy to follow;
import pygame
import sys
import time
import random
import emoji
from pygame.locals import*# set up some constantsWINDOWWIDTH =800WINDOWHEIGHT =600CELLSIZE =20FPS =15SNAKESPEED =15# calculate cell width and heightCELLWIDTH = int(WINDOWWIDTH / CELLSIZE)
CELLHEIGHT = int(WINDOWHEIGHT / CELLSIZE)
# set up the coloursWHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
DARKGREEN = (0, 100, 0)
DARKGRAY = (40, 40, 40)
BGCOLOR = (0, 0, 0) # Black background# set up the direction variablesUP ='up'DOWN ='down'LEFT ='left'RIGHT ='right'HEAD =0# index of the snake's headdefmain():
global FPSCLOCK, DISPLAYSURF, BASICFONT
pygame.init()
FPSCLOCK = pygame.time.Clock()
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
pygame.display.set_caption('Snake Game')
showStartScreen()
whileTrue:
runGame()
showGameOverScreen()
defrunGame():
# Set a random start point. startx = random.randint(5, CELLWIDTH -6)
starty = random.randint(5, CELLHEIGHT -6)
wormCoords = [{'x': startx, 'y': starty},
{'x': startx -1, 'y': starty},
{'x': startx -2, 'y': starty}]
direction = RIGHT
# Start the apple in a random place. apple = getRandomLocation()
whileTrue: # main game loopfor event in pygame.event.get(): # event handling loopif event.type == QUIT:
terminate()
elif event.type == KEYDOWN:
if (event.key == K_UP or event.key == K_w) and direction != DOWN:
direction = UP
elif (event.key == K_DOWN or event.key == K_s) and direction != UP:
direction = DOWN
elif (event.key == K_LEFT or event.key == K_a) and direction != RIGHT:
direction = LEFT
elif (event.key == K_RIGHT or event.key == K_d) and direction != LEFT:
direction = RIGHT
elif event.key == K_ESCAPE:
terminate()
# check if the Snake has hit itself or the edgeif wormCoords[HEAD]['x'] ==-1or wormCoords[HEAD]['x'] == CELLWIDTH or wormCoords[HEAD]['y'] ==-1or wormCoords[HEAD]['y'] == CELLHEIGHT:
return# game overfor wormBody in wormCoords[1:]:
if wormBody['x'] == wormCoords[HEAD]['x'] and wormBody['y'] == wormCoords[HEAD]['y']:
return# game over# check if Snake has eaten an applyif wormCoords[HEAD]['x'] == apple['x'] and wormCoords[HEAD]['y'] == apple['y']:
# don't remove worm's tail segment apple = getRandomLocation() # set a new apple somewhereelse:
del wormCoords[-1] # remove worm's tail segment# move the worm by adding a segment in the direction it is movingif direction == UP:
newHead = {'x': wormCoords[HEAD]['x'], 'y': wormCoords[HEAD]['y'] -1}
elif direction == DOWN:
newHead = {'x': wormCoords[HEAD]['x'], 'y': wormCoords[HEAD]['y'] +1}
elif direction == LEFT:
newHead = {'x': wormCoords[HEAD]['x'] -1, 'y': wormCoords[HEAD]['y']}
elif direction == RIGHT:
newHead = {'x': wormCoords[HEAD]['x'] +1, 'y': wormCoords[HEAD]['y']}
wormCoords.insert(0, newHead)
DISPLAYSURF.fill(BGCOLOR)
drawGrid()
drawWorm(wormCoords)
drawApple(apple)
drawScore(len(wormCoords) -3)
pygame.display.update()
FPSCLOCK.tick(SNAKESPEED)
defdrawPressKeyMsg():
pressKeySurf = BASICFONT.render('Press a key to play.', True, DARKGRAY)
pressKeyRect = pressKeySurf.get_rect()
pressKeyRect.topleft = (WINDOWWIDTH -200, WINDOWHEIGHT -30)
DISPLAYSURF.blit(pressKeySurf, pressKeyRect)
defcheckForKeyPress():
if len(pygame.event.get(QUIT)) >0:
terminate()
keyUpEvents = pygame.event.get(KEYUP)
if len(keyUpEvents) ==0:
returnNoneif keyUpEvents[0].key == K_ESCAPE:
terminate()
return keyUpEvents[0].key
defshowStartScreen():
titleFont = pygame.font.Font('freesansbold.ttf', 100)
titleSurf1 = titleFont.render('Snake Game!', True, WHITE, DARKGREEN)
degrees1 =0 degrees2 =0whileTrue:
DISPLAYSURF.fill(BGCOLOR)
rotatedSurf1 = pygame.transform.rotate(titleSurf1, degrees1)
rotatedRect1 = rotatedSurf1.get_rect()
rotatedRect1.center = (WINDOWWIDTH /2, WINDOWHEIGHT /2)
DISPLAYSURF.blit(rotatedSurf1, rotatedRect1)
drawPressKeyMsg()
if checkForKeyPress():
pygame.event.get() # clear event queuereturn pygame.display.update()
FPSCLOCK.tick(FPS)
degrees1 +=3# rotate by 3 degrees each frame degrees2 +=7# rotate by 7 degrees each framedefterminate():
pygame.quit()
sys.exit()
defgetRandomLocation():
return {'x': random.randint(0, CELLWIDTH -1), 'y': random.randint(0, CELLHEIGHT -1)}
defdrawGrid():
for x in range(0, WINDOWWIDTH, CELLSIZE): # draw vertical lines pygame.draw.line(DISPLAYSURF, DARKGRAY, (x, 0), (x, WINDOWHEIGHT))
for y in range(0, WINDOWHEIGHT, CELLSIZE): # draw horizontal lines pygame.draw.line(DISPLAYSURF, DARKGRAY, (0, y), (WINDOWWIDTH, y))
defdrawWorm(wormCoords):
for coord in wormCoords:
x = coord['x'] * CELLSIZE
y = coord['y'] * CELLSIZE
wormSegmentRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)
pygame.draw.rect(DISPLAYSURF, DARKGREEN, wormSegmentRect)
wormInnerSegmentRect = pygame.Rect(x +4, y +4, CELLSIZE -8, CELLSIZE -8)
pygame.draw.rect(DISPLAYSURF, GREEN, wormInnerSegmentRect)
defdrawApple(coord):
x = coord['x'] * CELLSIZE
y = coord['y'] * CELLSIZE
appleRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)
pygame.draw.rect(DISPLAYSURF, RED, appleRect)
defdrawScore(score):
scoreSurf = BASICFONT.render('Score: %s'% (score), True, WHITE)
scoreRect = scoreSurf.get_rect()
scoreRect.topleft = (WINDOWWIDTH -120, 10)
DISPLAYSURF.blit(scoreSurf, scoreRect)
defshowGameOverScreen():
gameOverFont = pygame.font.Font('freesansbold.ttf', 150)
gameSurf = gameOverFont.render('Game', True, WHITE)
overSurf = gameOverFont.render('Over', True, WHITE)
gameRect = gameSurf.get_rect()
overRect = overSurf.get_rect()
gameRect.midtop = (WINDOWWIDTH /2, 10)
overRect.midtop = (WINDOWWIDTH /2, gameRect.height +10+25)
DISPLAYSURF.blit(gameSurf, gameRect)
DISPLAYSURF.blit(overSurf, overRect)
drawPressKeyMsg()
pygame.display.update()
pygame.time.wait(500)
checkForKeyPress() # clear out any key presses in the event queuewhileTrue:
if checkForKeyPress():
pygame.event.get() # clear event queuereturnif __name__ =='__main__':
main()
As you can see Aider is is simple to use and I suspect is way more powerful than I have shown here, I am going to spend some more time playing with it and see what else it can do.
If you want to learn more about Aider Chat, you can more examples here↗and a GitHub repo containing the code and full chat history I generated for this post here↗.