Pair programming a game with GitHub Copilot and a human

This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Community Hub.

 

Chris_Noring_1-1693589156528.jpg

We've heard a lot about GitHub Copilot, but maybe more specifically about LLMs, large language models and how they can be used to generate code. You might even have used ChatGPT.

GitHub Copilot chat, is a product built by GitHub, it relies on a specific type of LLm a so called codex model and integrates with your IDE. It's a bit like a pair programmer, but one that has seen a lot of code and can help you write it.

So what will we do today? We'll use GitHub Copilot chat to solve a problem. The problem we have is Rock Paper Scissors. It's a small game that most people knows the rules to. It's also an interesting problem as it's small and contained, but still has some complexity to it.

Where do we start? The interesting part here is that there are many ways to start which I discovered speaking to my colleague Cynthia. What we're doing today is based on the excellent challenge module by Cynthia.
 
 
References

 
Approaches
So what approach do we choose?

- Domain description. In this version, we write a domain descriptions with all rules and concepts in it and feed that to our AI pair programmer.
- One comment at a time. Here, we write a comment and gradually work our towards a solution. In this approach we tackle one concept and rule at a time.

For the sake of this article, we'll use the domain description approach.

Solving the problem: use domain description

Luckily for us, the training module already have a domain description, here it is:

Game rules: Rock beats scissors (breaking it). Scissors beat paper (cutting it). Paper beat rock (wrapping it). The minigame is multiplayer and the computer plays the role of your opponent and chooses a random element from the list of elements Interaction with the player: The console is used to interact with the player. The player can choose one of the three options: rock, paper, or scissors. The player can choose whether to play again. The player should be warned if they enter an invalid option. The player is shown their score at the end of the game. Validation of user input: At each round, the player must enter one of the options in the list and be informed if they won, lost, or tied with the opponent. The minigame must handle user inputs, putting them in lowercase and informing the user if the option is invalid. By the end of each round, the player must answer whether they want to play again or not.

> You can find this description in the

Ok, now what? Now we do the following:

1. Create a new file called rockpaperscissor.py and paste the domain description at the top like so:
 
   # rockpaperscissor.py # Game rules: # Rock beats scissors (breaking it). # Scissors beat paper (cutting it). # Paper beat rock (wrapping it). # The minigame is multiplayer and the computer plays the role of your opponent and chooses a random element from the list of elements # Interaction with the player: # The console is used to interact with the player. # The player can choose one of the three options: rock, paper, or scissors. # The player can choose whether to play again. # The player should be warned if they enter an invalid option. # The player is shown their score at the end of the game. # Validation of user input: # At each round, the player must enter one of the options in the list and be informed if they won, lost, or tied with the opponent. # The minigame must handle user inputs, putting them in lowercase and informing the user if the option is invalid. # By the end of each round, the player must answer whether they want to play again or not. # game loop where the player and computer selects rock, paper, or scissors, round is repeated until player quits by typing "quit" ​
 

   Now we've provided context to Copilot in two ways, both by providing a domain description and by providing a file name *rockpaperscissor.py*. In the next step, let's see what Copilot can do for us.

1. Type the following code:

   quit = False while  

    Here's where I stopped typing code because Copilot suggested the following:
 
   not quit:

    and for every Enter I pressed, Copilot suggested code. So after having typed Enter a number of times and accepting all completions I arrived at this code:

   quit = False while not quit: # player input player = input("rock, paper, or scissors? ") # computer input import random computer = random.choice(["rock", "paper", "scissors"]) # game logic if player == computer: print("tie") elif player == "rock": if computer == "paper": print("computer wins") else: print("player wins") elif player == "paper": if computer == "scissors": print("computer wins") else: print("player wins") elif player == "scissors": if computer == "rock": print("computer wins") else: print("player wins") else: print("invalid input") # ask player if they want to play again play_again = input("play again? (y/n) ") if play_again == "n": quit = True elif play_again == "y": quit = False else: print("invalid input") quit = True
 
Improve the code, step 1

This code works, but it's not the greatest code to look at. It doesn't keep track of scores and it's not very readable. So let's see if we can improve it.

Open the the Chat component by selecting it at the bottom left of your left side menu (looks like a chat window). In it, type the prompt "improve code". It should generate a response like so:

One improvement you could make is to add a score counter for the player and computer, so that the player can see their score at the end of the game. Here's an updated version of the code with a score counter:

followed by the code:
 
# Game rules: # Rock beats scissors (breaking it). # Scissors beat paper (cutting it). # Paper beat rock (wrapping it). # The minigame is multiplayer and the computer plays the role of your opponent and chooses a random element from the list of elements # Interaction with the player: # The console is used to interact with the player. # The player can choose one of the three options: rock, paper, or scissors. # The player can choose whether to play again. # The player should be warned if they enter an invalid option. # The player is shown their score at the end of the game. # Validation of user input: # At each round, the player must enter one of the options in the list and be informed if they won, lost, or tied with the opponent. # The minigame must handle user inputs, putting them in lowercase and informing the user if the option is invalid. # By the end of each round, the player must answer whether they want to play again or not. # initialize score counters player_score = 0 computer_score = 0 # game loop where the player and computer selects rock, paper, or scissors, round is repeated until player quits by typing "quit" quit = False while not quit: # player input player = input("rock, paper, or scissors? ") # computer input import random computer = random.choice(["rock", "paper", "scissors"]) # game logic if player == computer: print("tie") elif player == "rock": if computer == "paper": print("computer wins") computer_score += 1 else: print("player wins") player_score += 1 elif player == "paper": if computer == "scissors": print("computer wins") computer_score += 1 else: print("player wins") player_score += 1 elif player == "scissors": if computer == "rock": print("computer wins") computer_score += 1 else: print("player wins") player_score += 1 else: print("invalid input") # ask player if they want to play again play_again = input("play again? (y/n) ") if play_again == "n": quit = True elif play_again == "y": quit = False else: print("invalid input") quit = True # print final scores print("player score:", player_score) print("computer score:", computer_score)

In the above code, we've added a score counter for the player and computer, so that the player can see their score at the end of the game. We've also added a print statement to print the final scores.

Improve the code, step 2, use your know-how as a developer

What else can we do? At this point, we can start to refactor the code. An improvement we can do is to add a so called rule matrix. A rule matrix is a matrix that contains all the rules of the game. It's a bit like a lookup table. Let's provide that as a prompt to Copilot.

Here's what we're typing in Copilot chat:
 
improve code by adding a rule matrix to compare player and computer choices

which results in the following output from Copilot:
 
# Game rules: # Rock beats scissors (breaking it). # Scissors beat paper (cutting it). # Paper beat rock (wrapping it). # The minigame is multiplayer and the computer plays the role of your opponent and chooses a random element from the list of elements # Interaction with the player: # The console is used to interact with the player. # The player can choose one of the three options: rock, paper, or scissors. # The player can choose whether to play again. # The player should be warned if they enter an invalid option. # The player is shown their score at the end of the game. # Validation of user input: # At each round, the player must enter one of the options in the list and be informed if they won, lost, or tied with the opponent. # The minigame must handle user inputs, putting them in lowercase and informing the user if the option is invalid. # By the end of each round, the player must answer whether they want to play again or not. # initialize score counters player_score = 0 computer_score = 0 # rule matrix rules = { "rock": {"rock": "tie", "paper": "lose", "scissors": "win"}, "paper": {"rock": "win", "paper": "tie", "scissors": "lose"}, "scissors": {"rock": "lose", "paper": "win", "scissors": "tie"} } # game loop where the player and computer selects rock, paper, or scissors, round is repeated until player quits by typing "quit" quit = False while not quit: # player input player = input("rock, paper, or scissors? ") # computer input import random computer = random.choice(["rock", "paper", "scissors"]) # game logic if player not in rules: print("invalid input") else: result = rules[player][computer] if result == "win": print("player wins") player_score += 1 elif result == "lose": print("computer wins") computer_score += 1 else: print("tie") # ask player if they want to play again play_again = input("play again? (y/n) ") if play_again == "n": quit = True elif play_again == "y": quit = False else: print("invalid input") quit = True # print final scores print("player score:", player_score) print("computer score:", computer_score)
 
Now, you see how the code looks a lot better, no more nested if statements. We've also added a rule matrix (technically it added a dictionary not a matrix, but it's still a big improvement over the code that was there before) to compare player and computer choices. Of course, we can still improve the code. For example, we can add a function to print the final scores. We should also add tests and documentation before we call it a day.

Conclusion

Sometimes using an AI assistant may look like magic, we could give it a full domain description. That could be a valid approach if the problem is small enough. Even then you need to improve the code in stages.

Another learning is that the more knowledge you have of code in general and the problem domain, the better you can guide the AI assistant to arrive at the solution you want.

Compare the difference between the first and last attempt, the last attempt is much better, don't you think?

AI assistants aren't here to replace us YET, but to help us solve problems, we still need to guide, we still need to know what we're doing. But they can help us solve problems faster and better.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.