pythonwhizzkid
pythonwhizzkid

Reputation: 91

Double dice graphics in Python

I'm new to programming and in my project, I'm trying to print basic dice graphics.

I'm trying to make a function that accepts two numbers from 1 to 6, and prints corresponding two dice faces next to each other. I've tried several approaches, but this is the only one that worked, and it's quite chunky:

s="+ - - - - +   + - - - - +"
m1="|  o   o  |"
m2="|  o      |"
m3="|    o    |"
m4="|      o  |"
m5="|         |"

def dice(a,b):
    if a == 1:
        str1=m5
        str2=m3
        str3=m5
    elif a == 2:
        str1=m2
        str2=m5
        str3=m4
    elif a == 3:
        str1=m2
        str2=m3
        str3=m4
    elif a == 4:
        str1=m1
        str2=m5
        str3=m1
    elif a == 5:
        str1=m1
        str2=m3
        str3=m1
    elif a == 6:
        str1=m1
        str2=m1
        str3=m1
    if b == 1:
        str1=str1+"   "+m5
        str2=str2+"   "+m3
        str3=str3+"   "+m5
    elif b == 2:
        str1=str1+"   "+m2
        str2=str2+"   "+m5
        str3=str3+"   "+m4
    elif b == 3:
        str1=str1+"   "+m2
        str2=str2+"   "+m3
        str3=str3+"   "+m4
    elif b == 4:
        str1=str1+"   "+m1
        str2=str2+"   "+m5
        str3=str3+"   "+m1
    elif b == 5:
        str1=str1+"   "+m1
        str2=str2+"   "+m3
        str3=str3+"   "+m1
    elif b == 6:
        str1=str1+"   "+m1
        str2=str2+"   "+m1
        str3=str3+"   "+m1
    print(s)
    print(str1)
    print(str2)
    print(str3)
    print(s)

Is there a more compact and elegant way to do this?

Upvotes: 8

Views: 2884

Answers (7)

Rohit
Rohit

Reputation: 1

def dice_face(num):

comb=["┌─────────┐","│  ●      │","│  ●   ●  │","│    ●    │", "│      ●  │","│         │","└─────────┘"]
line2= comb[5] if num==1  else comb[1] if num in [2,3] else comb[2]

line3= comb[3] if num in [1,3,5] else comb[5] if num in [2,4] else comb[2]

line4= comb[2] if num in [4,5,6] else comb[4] if num in [2,3] else comb[5]

face=[comb[0],line2,line3,line4,comb[6]]
return face

Here's beginner approach store all possible combinations in a list and use nested if else for middle lines with dots, this returns a list use loop to print dice.

def multi_dice(num,num2):
    for i in range(5):
        print(dice_face(num)[i],dice_face(num2)[i])

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1122382

Don't try to generate the lines of two dice at once; Python string processing is powerful enough to make joining two blocks of lines together to have them work side by side. Focus on efficiently generate the dice face instead.

You don't need to generate every line in a dice. Dice are symmetrical; the bottom half is a rotation of the top half. You can create one half purely with a few boolean tests to decide between an eye and not an eye; here are the dice values for when the three eyes in a half (ignoring the middle eye) are on:

+ - - - - - - - - - - +
| {2,3,4,5,6} {4,5,6} |
|
|     {6}

The middle eye is there when the number is odd. So you can determine if an eye is present with value > 1, value > 3 and value > 5, respectively.

The following version generates such a half without the central eye, then combines two halves ('rotating' the second half by reversing the string), and inserting the middle eye between them:

def ascii_dice(v, e=' o'):
    half = f'+ - - - - +\n|  {e[v > 1]}   {e[v > 3]}  |\n|  {e[v > 5]} '
    return f'{half}{e[v & 1]}{half[::-1]}'

This produces any of the 6 dice faces on demand:

>>> for i in range(1, 7):
...     print(ascii_dice(i))
...
+ - - - - +
|         |
|    o    |
|         |
+ - - - - +
+ - - - - +
|  o      |
|         |
|      o  |
+ - - - - +
+ - - - - +
|  o      |
|    o    |
|      o  |
+ - - - - +
+ - - - - +
|  o   o  |
|         |
|  o   o  |
+ - - - - +
+ - - - - +
|  o   o  |
|    o    |
|  o   o  |
+ - - - - +
+ - - - - +
|  o   o  |
|  o   o  |
|  o   o  |
+ - - - - +

Next, combining multiple dice next to each other is simply a function of splitting up multiple blocks of lines and grouping the lines of each block; all the first lines together, second lines together, etc, then rejoining those with a space in between. That's a job zip() and str.join() are very good at; the following function can produce any number of dice in a row:

def multi_dice(*d, sep='   '):
    zipped = zip(*(ascii_dice(v).splitlines() for v in d))
    return '\n'.join([sep.join(l) for l in zipped])

The zip(*(...)) function does all the work here, it is passed the lines of each dice face (as a list of string without newlines) as separate arguments, and does all the pairing up of first lines, second lines, etc. We then join those sections of dice faces with the sep variable (3 spaces by default) and join all the resulting lines with newlines.

Now you can print as many dice next to each other as you could want:

>>> print(multi_dice(*random.sample(range(1, 7), 6)))
+ - - - - +   + - - - - +   + - - - - +   + - - - - +   + - - - - +   + - - - - +
|  o      |   |  o   o  |   |  o   o  |   |  o      |   |         |   |  o   o  |
|         |   |  o   o  |   |    o    |   |    o    |   |    o    |   |         |
|      o  |   |  o   o  |   |  o   o  |   |      o  |   |         |   |  o   o  |
+ - - - - +   + - - - - +   + - - - - +   + - - - - +   + - - - - +   + - - - - +

For your use case, just pass in 2 dice values, and print what the function returns:

>>> print(multi_dice(4, 5))
+ - - - - +   + - - - - +
|  o   o  |   |  o   o  |
|         |   |    o    |
|  o   o  |   |  o   o  |
+ - - - - +   + - - - - +

Upvotes: 1

Andrey Tyukin
Andrey Tyukin

Reputation: 44918

Here is a way that's definitely more compact:

print("\u2680\u2681\u2682\u2683\u2684\u2685")

prints:

⚀⚁⚂⚃⚄⚅

There are already Unicode symbols for that! Just save them in a string, and when asked to give i-th die, return the i-1-th character:

def die(i):
  return "\u2680\u2681\u2682\u2683\u2684\u2685"[i - 1]

def dice(i, j):
  print(f"{die(i)}{die(j)}")

Example:

>>> dice(2, 3)
⚁⚂

Upvotes: 4

martineau
martineau

Reputation: 123473

Although you could do something really fancy, for such a relatively simple case something like this using some hardcoded data would be probably be fine (and changing how they look would also be very easy to do):

DICE_DATA = """\
+ - - - - +,+ - - - - +,+ - - - - +,+ - - - - +,+ - - - - +,+ - - - - +
|         |,|  o      |,|  o      |,|  o   o  |,|  o   o  |,|  o   o  |
|    o    |,|         |,|    o    |,|         |,|    o    |,|  o   o  |
|         |,|      o  |,|      o  |,|  o   o  |,|  o   o  |,|  o   o  |
+ - - - - +,+ - - - - +,+ - - - - +,+ - - - - +,+ - - - - +,+ - - - - +
"""

faces = [[] for _ in range(6)]
for line in DICE_DATA.splitlines():
    for i, section in enumerate(line.split(',')):
        faces[i].append(section)

for face in faces:
    print('\n'.join(face))  # Print a single face.

Output:

+ - - - - +
|         |
|    o    |
|         |
+ - - - - +
+ - - - - +
|  o      |
|         |
|      o  |
+ - - - - +
+ - - - - +
|  o      |
|    o    |
|      o  |
+ - - - - +
+ - - - - +
|  o   o  |
|         |
|  o   o  |
+ - - - - +
+ - - - - +
|  o   o  |
|    o    |
|  o   o  |
+ - - - - +
+ - - - - +
|  o   o  |
|  o   o  |
|  o   o  |
+ - - - - +

Upvotes: 3

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476740

You're attempt shows already some good parts: finding common parts, and store these in separate variables.

But we can do a better job by implementing a single function that generates one die. For example:

s ="+ - - - - +"
m1="|  o   o  |"
m2="|  o      |"
m3="|    o    |"
m4="|      o  |"
m5="|         |"

dice = [
    [m5, m3, m5],
    [m2, m5, m4],
    [m2, m3, m4],
    [m1, m5, m1],
    [m1, m3, m1],
    [m1, m1, m1]
]

so now we can make a function for one die with:

def die(i):
    return [s, *dice[i-1], s]

This will, for a given i in the range, return a list containing three strings.

We can then make function that joins the lines together, like:

def join_row(*rows):
    return ['   '.join(r) for r in zip(*rows)]

So now for two dice, we can define a function like:

def twodice(a, b):
    for line in join_row(die(a), die(b)):
        print(line)

The nice thing is that we can generalize this for any number of dice, for example:

def ndice(*ns):
    for line in join_row(*map(die, ns)):
        print(line)

For example:

>>> ndice(3, 2, 5, 1)
+ - - - - +   + - - - - +   + - - - - +   + - - - - +
|  o      |   |  o      |   |  o   o  |   |         |
|    o    |   |         |   |    o    |   |    o    |
|      o  |   |      o  |   |  o   o  |   |         |
+ - - - - +   + - - - - +   + - - - - +   + - - - - +
>>> ndice(1)
+ - - - - +
|         |
|    o    |
|         |
+ - - - - +
>>> ndice(1, 4)
+ - - - - +   + - - - - +
|         |   |  o   o  |
|    o    |   |         |
|         |   |  o   o  |
+ - - - - +   + - - - - +
>>> ndice(1, 4, 2)
+ - - - - +   + - - - - +   + - - - - +
|         |   |  o   o  |   |  o      |
|    o    |   |         |   |         |
|         |   |  o   o  |   |      o  |
+ - - - - +   + - - - - +   + - - - - +
>>> ndice(1, 4, 2, 5)
+ - - - - +   + - - - - +   + - - - - +   + - - - - +
|         |   |  o   o  |   |  o      |   |  o   o  |
|    o    |   |         |   |         |   |    o    |
|         |   |  o   o  |   |      o  |   |  o   o  |
+ - - - - +   + - - - - +   + - - - - +   + - - - - +

A nice thing of this approach is that you get a lot of utility functions with this that you can reuse for similar problems. Furthermore each function does simple things, and thus the odds that there are huge problems with one of the functions is rather "low". If problems occur, it is typically easy to fix these.

Upvotes: 15

PM 2Ring
PM 2Ring

Reputation: 55479

Here's another variation on the theme. I encode the face patterns as numeric strings.

rows = (
    "+ - - - - +",
    "|  o   o  |",
    "|  o      |",
    "|    o    |",
    "|      o  |",
    "|         |",
)

faces = ['555', '535', '254', '234', '151', '131', '111']

def dice(n):
    out = [rows[0]]
    for u in faces[n]:
        out.append(rows[int(u)])
    out.append(rows[0])
    return out

def multi_dice(*nums):
    buf = [[] for _ in range(5)]
    for row, seg in zip(buf, zip(*[dice(n) for n in nums])):
        row.extend(seg)
    return '\n'.join(map('   '.join, buf))

# Test showing all 6 faces
print(multi_dice(1, 2, 3, 4, 5, 6))

output

+ - - - - +   + - - - - +   + - - - - +   + - - - - +   + - - - - +   + - - - - +
|         |   |  o      |   |  o      |   |  o   o  |   |  o   o  |   |  o   o  |
|    o    |   |         |   |    o    |   |         |   |    o    |   |  o   o  |
|         |   |      o  |   |      o  |   |  o   o  |   |  o   o  |   |  o   o  |
+ - - - - +   + - - - - +   + - - - - +   + - - - - +   + - - - - +   + - - - - +

We can reduce the dice function to one line, using a list comprehension:

def dice(n):
    return [rows[0]] + [rows[int(u)] for u in faces[n]] + [rows[0]]

As a bonus, if you pass n=0 to dice, you get a blank face.

Upvotes: 1

jsbueno
jsbueno

Reputation: 110311

You can write the string representations of all dice as a dictionary, with the keys from 1 to 6. As value, you can use a list of strings, composing the dice.

When printing, you pick lines from each set of values, according to the keys.

As on how to create the dictionaries, the approach can be like the one you took there, but it is made at once, without all the ifs -

s= "+ - - - - +"
m1="|  o   o  |"
m2="|  o      |"
m3="|    o    |"
m4="|      o  |"
m5="|         |"

die = {
  1: [s, m5, m3, m5, s],
  2: ...,
  ...
  6: [s, m2, m2, m2, s]
}

def print_dice(a, b):
    for part1, part2 in zip(die[a], die[b)):
         print (part1, part2)

The zip is responsible for, given two or more sequences or iterators, pick an element from each and yield a tuple with each element. The print function itself can print both parts of the dices.

And if insetead of two dices you want any number of them, you just have to use the "*" syntax in Python, it will work both in the function parameters, in the call to zip, and call to print:

def print_n_dice(*args):
    for parts in zip(*(die[x] for x in args)):
         print(*parts)

Upvotes: 2

Related Questions