How to Code a Yahtzee Easy Javescriopt

A game of random dice

Photo by hidde schalm on Unsplash

Maybe you found this piece because you're solving an assignment at school. Checking dice for results in Yahtzee is a popular task for Python classes. It's also a really fun one because there are so many different things to check for.

In this article, I'll guide you through most of the code you need to build a functional Yahtzee game. We're going to write the whole thing in Python. The user interacts with the game on their keyboard in the terminal.

I started doing some research on Yahtzee and I found some interesting information. Apparently, it's played differently throughout the world.

To my surprise, there were several students in my class who had never played Yahtzee. So here's a brief introduction to the game.

The gameplay goes like this:

  1. Roll five dice.
  2. Keep the ones you want.
  3. Roll the ones you decided not to keep. This is your second roll.
  4. Keep the ones you want from your second roll and add them to the keep pile.
  5. Roll one last time. This is your third roll and you are forced to keep these dice.
  6. Now that you have your final stack of dice, you check if it's valid. Then you add your total score, by counting all the dots on the dice, to that field. Say you roll a — "full house" (3 + 2) 6, 6, 6, 5, 5 — you then add 28 to the "Full House" field.

Here's a more visual representation:

The Scoreboard

The scoreboard is split into two sections. The first section is about collecting aces(1), twos, threes, fours, fives, and sixes. If you roll three twos, you add the value six to the scoreboard on your twos section.

If your total in the first section is ≥ 63, you get an additional bonus of 50 points.

The lower section has more fun combinations:

  • 1 pair
  • 2 pairs
  • 3 of a kind
  • 4 of a kind
  • Full House (3+2)
  • Low Straight (range from 1–5)
  • High Straight (range from 2–6)
  • Yahtzee (All dice the same, 50 points no matter what value the dice)
  • Chance (any dice, just add up the total. This is a safety net when you don't get anything)

We don't have Yahtzee bonus in Norway. I have seen that some places, but I won't include it in this program.

Let's Code!

Photo by NEXT Academy on Unsplash

Here is what I am thinking before even writing any code:

  • The score will be a dictionary holding all the values.
  • We will create the functions, checking what you rolled in a Roll class and then use this to populate the dictionary.
  • We want the following interaction with the user:

We are going to do this in reverse order. Let's start by creating the functions that check if the dice fit into any of the categories.

Roll

Here is the current shell for the class Roll (so far we're just focusing on finding results of the rolls, not how the rolling of the dice actually works):

def check_yatzy()

Let's start with Yahtzee. This one is actually quite easy. We pass a list as an argument and we know that all the items on that list will have exactly the same value.

If you're not familiar with sets, you may be thinking you could simply loop through the list and check if the next item is the same as the last one. You could — but using sets is so much easier. Take a look:

              def check_yatzy(self, dice_list):                              if len(set(dice_list)) == 1:
return True
return False

That's it! In a set, you can't have any equal items — so, if they're all the same, you end up with just a single item. If the length of that set is one — you have Yahtzee. Congratulations!

Check your code

Don't forget to check your code as you write it. If you wait until it is all done it can be really painful to fix errors. I check my code in two different ways: Visually, using print:

              dices = Roll()
print (f'is it Yatzy? {dices.check_yatzy([1,1,1,1,1])}')

Or, the behind-the-scenes way with assert:

              dices = Roll()
assert dices.check_yatzy([1,1,1,1,1]) == True
assert dices.check_yatzy([1,1,1,2,1]) == False

With print you can write a proper text, so it's easier for you to read the tests. With assert you get an actual error. Pick the one you like best.

You might want to throw in an intentional error as well:

              assert dices.check_yatzy([1,1,1,2,5]) == True            

def check_full_house()

Full house also works with sets, but there are some extra steps to ensure we don't mistake it for four of a kind:

A couple of clever things are going on here:

  • By sorting the list we have more control over the items. This way we can exclude loops from some of the checks.
  • Since we need 3+2 to make a full house it might be tempting to use the same code as with Yatzy and just ask if there are two items in the set. However, four of a kind will also have a set length of 2 (because it has 4+1).
  • We can either check for the three different ways we can roll a full house (XX000 , X000X , 000XX) or we can ask whether it is not the two ways you can roll four of a kind (XXXX0 , 0XXXX)

For these exercises, it can be a good idea to draw out possible scenarios. Something I learned last year was that it can be smarter to check for the situations it can not be rather than the situations it can be. This is, of course, a case-by-case decision.

def check_high_straight()

High straight is a range from 2–6. (2,3,4,5,6). This means that, once again, we can use a set to check whether there's a set length of 5. If the set length is 5, it means that we have no dice with equal value.

The only thing we have to do is to check if it is a low or a high straight. If we sort the list, we have many ways to check this. I chose to check the last item, and the first item in the list.

              def check_high_straight(self, dice_list):              dice_list.sort()
if len(set(dice_list)) == 5 and dice_list[4] == 6 and dice_list[0] == 2:
return True
return False

One thing that you want to consider writing code like this is how "locked" you want the code to be. If we write something along the lines of:

dice_list[len(dice_list)-1] instead of dice_list[4], we make sure we can support the game Maxi Yahtzee as well (In Maxi Yahtzee you have six dice instead of five.)

We won't do this here, but it's good practice to think about how you can use the code in as many settings as possible.

def check_low_straight()

You can work out how we do this one, simply by altering the previous function.

def check_four_kind()

For this one, it might be tempting to use set again, to check if the set length is two. However, that would exclude four of a kind if the player rolls Yahtzee. If you roll Yahtzee you also have four of a kind.

There are two ways you can roll four of a kind: XXXX0 and 0XXXX. All I have to check after I sort the list is whether [0] and [3] are the same or [1] and [4] are the same.

              def check_four_kind(self, dice_list):                              dice_list.sort()
if dice_list[0] == dice_list[3] or dice_list[1] == dice_list[4]:
return True
return False

def check_three_kind()

Checking three of a kind uses the method we've already touched upon.

We're sorting the list to make it easier for us to work on and then we check all the ways we can get three of a kind (00XXX , 0XXX0 , XXX00). Can you see how sorting helps us out here? We don't have to worry about something like this: X0X0X or 0XX0X.

              def check_three_kind(self, dice_list):

dice_list.sort()
if dice_list[0] == dice_list[2] or dice_list[1] == dice_list[3] or dice_list[2] == dice_list[4]:
return True
return False

check_two_pairs()

If we sort the list again, there are three ways we can roll two pairs. XXYY0, XX0YY, 0XXYY. This means we have to check three possible states.

              def check_two_pairs(self, dice_list):

dice_list.sort()
if (dice_list[0] == dice_list[1] and dice_list[2] == dice_list[3]) or (dice_list[0] == dice_list[1] and dice_list[3] == dice_list[4]) or (dice_list[1] == dice_list[2] and dice_list[3] == dice_list[4]):
return True
return False

check_one_pair()

Here's another one I'm going to let you figure out for yourself!

def check_single_value(self, dice_list, check_value)

Remember the first part of the game? You roll aces, twos, and so on. Well, when you do the same action on different items, that's a hint that you can create a method function handling any input.

For this function, we leave dice_list as a parameter and introduce another one called check_value.

This means we can pass a list as dice_list and then tell the method what number we're looking for.

              def single_values(self,dice_list,check_value):
roll_score = 0
for die in dice_list:
if die == check_value:
roll_score +=die
return roll_score

These two:

              print (f'score for aces? {dices1.single_values([1,1,1,1,1],1)}')
print (f'score for twos? {dices1.single_values([1,2,4,4,2],2)}')

will print this:

              score for aces? 5
score for twos? 4

Interacting with the methods

Now that we have these methods, we can start using them. If we're building a forced setup we know the order of the game:

  1. Start with aces, then twos, threes, fours, fives, sixes.
  2. Get the total score
  3. Is it a bonus?
  4. One pair, two pairs, three of a kind, four of a kind, full house, chance, Yahtzee.
  5. Total sum.

Game Logic

There are several things we need to make work before we can play the game.

Photo by Jukan Tateisi on Unsplash

We have two classes: Player and Roll. Then we have main, where we do all the gameplay.

We have all the checks working, now to get working on the dice logic. Here's the shell:

def roll_dice(self)

The first time we roll them, we know that there are five dice with a value ranging from one to six. There are many ways to do this, but I'll use one of my favorites — list comprehensions:

              def roll_dice(self):
self._current_kept_dice.clear()
self._current_dice_list = [random.randint(1,6) for die in range(0,5)]
print (f'you rolled {self._current_dice_list} ! \n')
return self._current_dice_list

The function needs to do two things: create the list and then return it. The print here is optional, but it's nice for the player to be able to immediately see what they rolled.

The reason I clear the kept list is that if we're doing a roll, we know it's the first roll and no dice can be kept. It's the initial roll of one round. If we don't clear it, we'll carry it over to the next round and the kept list will just keep growing.

def keep_dice(self)

This is a big one. We ask the user which dice they want to keep, then add that to the kept pile and return the initial roll minus what they decided to keep. First, let's look at the input:

              keep_input = input('which dice do you want to keep (comma separated: e.g. 1,1,5)? ')
split_input = keep_input.split(',')
#if the user types nothing, keep all:
if keep_input == '':
return self._current_dice_list
split_input_int = [int(item) for item in split_input] for die in split_input_int:
self._current_kept_dice.append(die)

Always make sure the user can keep typing in values until they get it correct. Read more about how you can do that in my article on input.

The code above allows the user to type in what they want to keep. If they roll [1,3,4,5,6] they can type 1, 5, for example, if he wants to keep those two. If he types nothing, we keep all of them for the next roll.

As you can see, the variable self._current_dice_roll is the one that's treated through all the rolling action.

We split the input and add them as integers into a list for further use.

The rest of the code in the function deals with splitting up the rolled dice and the ones we want to keep.

We loop through the input and check if it's also in the list of current dice. If it is, we kill it from the current dice list and return what's left for another roll. We also make sure the selection is passed to the variable self._current_kept_dice:

              for die in split_input_int:
self._current_kept_dice.append(die)
#cycle through list user wants to keep
for value in split_input_int:
if value in self._current_dice_list:
self._current_dice_list.remove(value)
return self._current_dice_list

I had to do some sketching to make sure I got my head around all this — especially the list removal from another list.

def reroll_dice(self, dice_list)

Just like roll, this creates a random value between one and six and returns a list with these. The only difference is that it's based on what's left of the original, after the user kept some dice:

              def reroll_dice(self, dice_list):
self._current_dice_list = [random.randint(1,6) for die in range(0,(len(dice_list)))]
print (f'You rolled {self._current_dice_list} ! \n')
return self._current_dice_list

def get_current_dice(self) and def get_kept_dice(self)

We have a couple of functions to fetch some information. These two will just return the different variables we have set up to store dice somehow.

              def get_current_dice(self):
return self._current_dice_list
def get_kept_dice(self):
return self._current_kept_dice

def forced_keep(self, dice_list)

This function will just force the roll to be added to the kept dice list. This is used after your third roll.

Putting it all together to play the game

The interaction we want looks like this:

main.py is the gameplay where we actually roll the dice. We can split this into two sections: one for the top part and one for the bottom part. Let's focus on the top for now.

              def main():
game_list_top = ['aces' , 'twos' , 'threes' , 'fours' , 'fives' , 'sixes']
game_list_top_values = [1,2,3,4,5,6]

The first list holds the human-readable names we want to check. The values are the value we are checking. We want to populate the player dictionary with "aces": three points if they roll 1, 1, 1.

The comments in the code files should explain the gameplay for you. Here are the latest and greatest code gists:

Player class

Most of the player class is just holding the scores and have modules that can store these values.

main.py

Roll class

Finish What We Started

Photo by Jeffrey F Lin on Unsplash

What I want you to do now is to write the rest of the game.

First, you need to wrap up the functions needed to check your dice list. Much of that we have done already.

Then you need to find a way to add the bottom part of the game to the gameplay. I would probably write the rolling into a function instead of having it directly in main().

You already have the top part and the bonus. When you're done with the bottom part, you can just loop through the dict and add up the sum.

Hint: we have already written a method that does exactly this, just with print.

The code does not account for user errors. Enjoy!

Thanks for reading!

collinsraveld.blogspot.com

Source: https://betterprogramming.pub/interview-questions-write-yahtzee-in-python-72695550d84e

0 Response to "How to Code a Yahtzee Easy Javescriopt"

إرسال تعليق

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel