Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appending miniature lists to a larger list

I'm creating a python application to play the game Perudo (or Liar's dice).

I am trying to create a function that calculates all the possible moves that the player (or AI) is allowed to make and returns a list of them so that it can reject illegal ones.

Turns are stored as a list of 2 numbers e.g. [10,6] representing ten sixes. If the starting variable currentbid is [19,3] (nineteen threes) and there are 20 dice in play, then the only possible moves are 19 fours, 19 fives, 19 sixes, 20 twos, 20 threes, 20 fours, 20 fives and 20 sixes. Calls of ones are not allowed. The program should output the above as:

[[19,4],[19,5],[19,6],[20,2],[20,3],[20,4],[20,5],[20,6]]

but instead outputs it as:

[[20,6],[20,6],[20,6],[20,6],[20,6],[20,6],[20,6],[20,6]]

What am I doing wrong?

def calcpossiblemoves(self, currentbid, totalnoofdice):
    self.possiblemoves = []  # Create a list of possible moves that will be added too

    self.bid = currentbid

    while self.bid[0] <= totalnoofdice:

        while self.bid[1] < 6:
            self.bid[1] += 1
            self.possiblemoves.append(self.bid)     # <- I think the problem is something to do with this line
            print(self.possiblemoves) #For tracking the process

        # Increase 1st number, reset 2nd number to 1

        self.bid[0] += 1
        self.bid[1] = 1 # Which will get increased to 2
        #print("Reached 6")
    return self.possiblemoves
like image 491
Tom Burrows Avatar asked Jun 28 '26 03:06

Tom Burrows


2 Answers

The problem is that you are using lists, which are mutable objects, and you're creating references, rather than copies. You could fix it by copying the self.bid list each time, but the most "pythonic" solution is not to use list, but to use tuples, instead:

def calcpossiblemoves(self, currentbid, totalnoofdice):
    possiblemoves = []

    numberof, value = currentbid

    while numberOf <= totalnoofdice:

        while value < 6:
            value += 1
            possiblemoves.append((numberOf, value))

        # Increase 1st number, reset 2nd number to 1

        numberof += 1
        value = 1 # Which will get increased to 2
        #print("Reached 6")
    return self.possiblemoves

Note that this doesn't update self.bid (but you can easily add that in), and you get a list of immutable tuples.

EDIT: Because tuples are immutable, I've used tuple unpacking to create two variables. This is to prevent the following problem:

>>> bid = (1, 2)
>>> bid[0] += 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

I could write bid = (bid[0]+1, bid[1]), but using two variables is, imho, easier to understand.


Generally, it's a good rule of thumb to make sure that all members of lists mean the same thing, whereas values meaning different things go in tuples or dicts, or custom containers such as NamedTuple or custom classes.

In this example, the outer list contains a number of bids, but each of the inner lists has a number of dice and a value, which indicates a list is perhaps the wrong type to use.

like image 97
RoadieRich Avatar answered Jun 29 '26 15:06

RoadieRich


You're better off in thinking about a base 6 number here where you effectively have a range of numbers for 20 dice of 0 - 126 inclusive.

current_bid = [19, 3]
offset = current_bid[0] * 6 + current_bid[1]
# 117 - and divmod(117, 6) == (19, 3)

So, to then get the valid bids that are left, you do the maths over the range of 117 - 126 and get the number of dice and remainder that's valid.

valid_bids_with1s = ([n // 6, n % 6 + 1] for n in range(offset, 126))
valid_bids = [[a, b] for a, b in valid_bids_with1s if b != 1]

Which gives you:

[[19, 4],
 [19, 5],
 [19, 6],
 [20, 2],
 [20, 3],
 [20, 4],
 [20, 5],
 [20, 6]]
like image 37
Jon Clements Avatar answered Jun 29 '26 16:06

Jon Clements