Enigmatic Code

Programming Enigma Puzzles

Enigma 1768: Die hard

From New Scientist #2936, 28th September 2013 [link]

I wanted a cube-shaped dice with each of three of its faces having a perfect square number of spots and each of the remaining faces having a prime number of spots. To follow convention I wanted each face to have a different number of spots and I also wanted opposite pairs of faces to add up to the same total.

To make this dice I took eight identical standard dice and stuck them together to form a cube of double the size. On closer investigation I found that none of the faces of four spots from the eight original dice was visible on the exterior of my new dice.

How many faces of one spot and how many faces of two spots from the original dice were visible on the exterior of my new dice?

Historically the singular of “dice” is “die”, but “dice” is now accepted as both a plural and a singular.

[enigma1768]

Advertisements

12 responses to “Enigma 1768: Die hard

  1. Jim Randell 25 September 2013 at 7:28 pm

    This isn’t the prettiest Python code, but it does find the answer in 85ms. I’ll try and clean it up later.

    from collections import namedtuple
    from itertools import product
    from enigma import printf
    
    # tuple to represent a die
    Die = namedtuple('Die', 'U D F B L R')
    
    # squares and primes between 4 and 24
    squares = [ 4, 9, 16 ]
    primes = [ 5, 7, 11, 13, 17, 19, 23 ]
    
    numbers = set(squares + primes)
    
    # all 3 squares must be used
    
    # minimum possible sum for opposite sides is 20 (= 16 + 4)
    # so 4 and 9 cannot be opposites
    
    # 4 must be 1 + 1 + 1 + 1, let's assume that's on the top
    # it cannot be opposite 9, so let's assume that's at the front
    
    # possible orientations of a die
    die = [
      #   U, D, F, B, L, R
      Die(1, 6, 2, 5, 3, 4),
      Die(1, 6, 3, 4, 5, 2),
      Die(1, 6, 5, 2, 4, 3),
      Die(1, 6, 4, 3, 2, 5),
      Die(2, 5, 1, 6, 4, 3),
      Die(2, 5, 4, 3, 6, 1),
      Die(2, 5, 6, 1, 3, 4),
      Die(2, 5, 3, 4, 1, 6),
      Die(3, 4, 1, 6, 2, 5),
      Die(3, 4, 2, 5, 6, 1),
      Die(3, 4, 6, 1, 5, 2),
      Die(3, 4, 5, 2, 1, 6),
      Die(4, 3, 1, 6, 5, 2),
      Die(4, 3, 5, 2, 6, 1),
      Die(4, 3, 6, 1, 2, 5),
      Die(4, 3, 2, 5, 1, 6),
      Die(5, 2, 1, 6, 3, 4),
      Die(5, 2, 3, 4, 6, 1),
      Die(5, 2, 6, 1, 4, 3),
      Die(5, 2, 4, 3, 1, 6),
      Die(6, 1, 2, 5, 4, 3),
      Die(6, 1, 4, 3, 5, 2),
      Die(6, 1, 5, 2, 3, 4),
      Die(6, 1, 3, 4, 2, 5),
    ]
    
    # choose a number to go opposite 4
    for o4 in numbers.difference((4, 9)):
      s = 4 + o4
      if s < 20: continue
      # what goes opposite 9?
      o9 = s - 9
      if o9 in (4, 9, o4) or o9 not in numbers: continue
      # and find the third pair
      for x in numbers.difference((4, o4, 9, o9)):
        ox = s - x
        if not(x < ox) or ox in (4, 9, o4, o9, x) or ox not in numbers: continue
    
        # now work out the decomposition of numbers
        
        # consider possible orientations of the top 4 dice
        for (d0, d1, d2, d3) in product(die[0:4], repeat=4):
          (f, b, l, r) = ((d2.F, d3.F), (d0.B, d1.B), (d0.L, d2.L), (d1.R, d3.R))
          if any(4 in x for x in (f, b, l, r)): continue
    
          # what's left for the front and back?
          (fr, br) = (9 - sum(f), o9 - sum(b))
          if fr < 2 or br < 2: continue
    
          # choose two die to complete the front
          for (d6, d7) in product(die, repeat=2):
            if fr != d6.F + d7.F: continue
            if 4 in (d6.L, d6.F, d6.D, d7.R, d7.F, d7.D): continue
    
            # and two to complete the back
            for (d4, d5) in product(die, repeat=2):
              if br != d4.B + d5.B: continue
              if o4 != d4.D + d5.D + d6.D + d7.D: continue
              if 4 in (d4.L, d4.B, d4.D, d5.R, d5.B, d5.D): continue
    
              # and check the sum of left and right sides
              (l, r) = (d0.L + d2.L + d4.L + d6.L, d1.R + d3.R + d5.R + d7.R)
              if not(min(l, r) == x and max(l, r) == ox): continue
    
              # visible faces
              v = (
                d0.U, d1.U, d2.U, d3.U, # U
                d4.D, d5.D, d6.D, d7.D, # D
                d2.F, d3.F, d6.F, d7.F, # F
                d0.B, d1.B, d4.B, d5.B, # B
                d0.L, d2.L, d4.L, d6.L, # L
                d1.R, d3.R, d5.R, d7.R, # R
              )
    
              printf("[s={s} (4, {o4}) (9, {o9}) ({x}, {ox}) {v}]")
              printf("1s={v1} 2s={v2}", v1=v.count(1), v2=v.count(2))
    

    Solution: There are 8 one-spot faces and 4 two-spot faces visible in the composite die.

    • Jim Randell 25 September 2013 at 11:45 pm

      If we consider the possible arrangements for the faces on the small dice that make up the vertices of the larger die, then we don’t need the code to reject faces on the small dice with 4-spots (as we can just not consider them). And we don’t need to determine possible face values for the larger die before starting to construct it (other than determining that 4 and 9 must be present and not on opposite faces). This shorter Python program takes a similar approach to the last part of my program above, but considers only the possibilities for the visible faces of the smaller dice. It runs in 67ms, although we could be a bit cleverer about selecting candidate vertices to sum to known numbers (rather than simply use itertools.product()), which would make the code longer again, but reduce the run time. And in that case it would probably also be faster to compute possible face values up front before constructing the composite die. But I prefer the more compact version below.

      from itertools import product
      from enigma import printf
      
      # consider the vertices of the small dice (defined by the three
      # surrounding faces) that form the 8 vertices of the large die
      
      # possible vertices (cannot include 4)
      vertices = (
        (1, 2, 3), (2, 3, 1), (3, 1, 2),
        (1, 3, 5), (3, 5, 1), (5, 1, 3),
        (2, 6, 3), (6, 3, 2), (3, 2, 6),
        (3, 5, 6), (5, 6, 3), (6, 3, 5),
      )
      
      # squares and primes between 4 and 24
      squares = set((4, 9, 16))
      primes = set((5, 7, 11, 13, 17, 19, 23))
      
      # consider possible arrangements of vertices on the top face (4)
      for (v0, v1, v2, v3) in product(vertices, repeat=4):
        u = v0[0] + v1[0] + v2[0] + v3[0]
        if u != 4: continue
        # and the remaining two vertices on the front face (9)
        for (v6, v7) in product(vertices, repeat=2):
          f = v2[1] + v3[2] + v6[2] + v7[1]
          if f != 9: continue
          # final two vertices
          for (v4, v5) in product(vertices, repeat=2):
            d = v4[0] + v5[0] + v6[0] + v7[0]
            b = v0[2] + v1[1] + v4[1] + v5[2]
            l = v0[1] + v2[2] + v4[2] + v6[1]
            r = v1[2] + v3[1] + v5[1] + v7[2]
            # check opposite faces sum to the same value
            if not(u + d == f + b == l + r): continue
            # and the numbers are all distinct and valid
            faces = (u, d, f, b, l, r)
            if len(squares.intersection(faces)) != 3: continue
            if len(primes.intersection(faces)) != 3: continue
      
            # output sum / face values / vertices
            printf("[s={s} / ({u}, {d}) ({f}, {b}) ({l}, {r}) / {v0} {v1} {v2} {v3} {v4} {v5} {v6} {v7}]", s=u + d)
      
            # collect visible faces
            v = v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7
            printf("1s={v1} 2s={v2}", v1=v.count(1), v2=v.count(2))
      
      • Jim Randell 23 October 2013 at 6:13 pm

        Here are two views of the composite die, showing all the visible faces of the smaller dice:

        Enigma 1768 - Solution

        The small dice are “identical”, so it is sufficient to consider one layout (or “handedness”). The other possible layout (with one pair of faces swapped over) would give a mirror image of this solution, but that doesn’t affect the overall answer to the problem.

  2. Brian Gladman 27 September 2013 at 11:20 am

    My version is slower than those above but it does not rely on any analysis of the opposite face sums. It also considers full dice rather than just their outer vertices as I didn’t want to figure out the die handedness issue manually.

    from itertools import combinations, product
    
    # Given two die faces anti-clockwise at a vertex, find the third
    # face at this vertex (using a western die when 'same' is True)
    def die_third_face(first, second, same=True):
      if second in (first, 7 - first):
        raise ValueError
      t, f = min(first, 7 - first), min(second, 7 - second)
      c1 = ((f - t) % 3 == (1 if same else 2))
      c2 = (first < 4) == (second < 4)
      return 6 - t - f if c1 == c2 else t + f + 1
    
    # Permute a die into its 24 spatial orientations (i.e. any of the six
    # faces on top and then any of the four side faces at the front).  In 
    # this enigma, don't allow 4 spots on any outer faces.
    def rotate(outer):
      die = dict()
      for left in range(1, 7):
        right = 7 - left
        for front in set(range(1, 7)) - set((left, right)):
          up = die_third_face(left, front)
          die = (left, front, up, right, 7 - front, 7 - up)
          if 'LFURBD'[die.index(4)] not in outer:
            yield dict(zip('LFURBD', die))
    
    # Yield the sets of pairs that can be formed from the 
    # even length sequence s
    def gen_pair_sets(s, r = tuple()):
      if len(s) <= 2:
        yield r + ((s[0], s[1]),)
      else:
        for i in range(1, len(s)):
          new_s = s[1:i] + s[i+1:]
          yield from gen_pair_sets(new_s, r +((s[0], s[i]),))
    
    # The primes and squares that might appear on the six faces
    pr = (5, 7, 11, 13, 17, 19, 23)
    sq = (4, 9, 16)
    
    # Yield the combinations of primes and squares that can
    # appear on the six faces
    def face_sums():
      # permute thye primes three at a time
      for p in combinations(pr, 3):
        # now add the squares to give the six
        # candidate values and from them into
        # sets of pairs
        for r in gen_pair_sets(sq + p):
          ss = [sum(t) for t in r]
          if ss[0] == ss[1] == ss[2]:
            yield r
    
    # The six dice faces are enumerated in the order: Left,
    # Front, Up, Right, Back, Down. The names of the dice:
    di = 'ULB URB ULF URF DLB DRB DLF DRF'.split()
    # and the eight dice as a dictionary
    die = dict()
    
    # for each possible set of face sums
    for (uu, dd), (ll, rr), (ff, bb) in face_sums():
      
      # combine all valid orientations of the four top dice
      for die['ULB'], die['URB'], die['ULF'], die['URF'] in product(
        rotate('ULB'), rotate('URB'), rotate('ULF'), rotate('URF')):
              
        # skip combinations if the upper face sum is incorrect
        if sum(die[x]['U'] for x in di if 'U' in x) != uu:
          continue
        
        # now combine all valid orientations of two bottom back dice
        for die['DLB'], die['DRB'] in product(rotate('DLB'), rotate('DRB')):
        
          # skip combinations if the back face sum is incorrect
          if sum(die[x]['B'] for x in di if 'B' in x) != bb:
            continue            
      
          # combine valid orientations of bottom, left, front die
          for die['DLF'] in rotate('DLF'):
      
            # check for the correct left face sum
            if sum(die[x]['L'] for x in di if 'L' in x) != ll:
              continue
            
            # combine valid orientations of bottom, right, front die
            for die['DRF'] in rotate('DRF'):
      
              # check for the correct right, front and down face sums
              if ( sum(die[x]['R'] for x in di if 'R' in x) != rr or
                   sum(die[x]['F'] for x in di if 'F' in x) != ff or
                   sum(die[x]['D'] for x in di if 'D' in x) != dd ):
                continue
              
              # output the dice orientations
              for d in di:
                print(d, die[d])
              # count the number of one and two spots on the outer faces
              t, u = [sum([die[d][x] for x in d].count(i) for d in di) 
                                          for i in (1, 2)]
              print('{:d} one spots and {:d} two spots.'.format(t, u))
    
    • Jim Randell 27 September 2013 at 11:34 am

      I don’t think there’s an issue with the “handedness” of the dice. We are given that they are all “identical”, so we can just consider one layout. If they were of the other layout you just get a mirror image solution, and that doesn’t change the overall answer to the puzzle.

      • Brian Gladman 27 September 2013 at 12:52 pm

        What I meant, Jim, is that I didn’t want to have to work out which of the three faces of the die at each vertex was on which face of the larger die manually.(the indexes into v0, v1 … needed to get the face sums (which are easy to get wrong at least when I work them out!). Sorry for my eaasily misunderstood comment.

  3. ahmet çetinbudaklar 20 October 2013 at 2:06 pm

    To meet the conditions of 3 prime numbers and 3 perfect squares on the six faces of the new dice which consists of 8 ordinary dice, we can easily see that the total of any opposite pair of faces must be 32 as a result of which one can reach to the solution.
    To make the new dice visible ı had to buy 8 ordinary dice and thereby construct the new dice..

    • Jim Randell 20 October 2013 at 2:13 pm

      If you’re saying that the sum of the number of spots on opposite faces of the composite die comes to 32, then I don’t think that that is correct.

      When the deadline for entries closes in a few days I’ll put up a diagram of the only possible solution.

      • ahmet çetinbudaklar 20 October 2013 at 2:31 pm

        Yes you are right Jim it is my silly mistake , however as ı cannot write the answer it is also in line with the rules that i made this mistake.

  4. geoffrounce 6 July 2017 at 5:14 pm

    There are 8 ones and 4 twos in the visible composite dice faces in the output at the end of my code – I thought the programme was long enough any way not to find these values programmatically.

    The three primes are 7,11 and 13 and the three squares are 4,9 and 16.

    The Up/Down faces add up to twenty, as do the Left/Right and Front/Back faces of the composite dice. It ended up as quite long code, but relatively fast.

    % A Solution in MiniZinc
    %         Top Dice Layer
    %        =============== 
    %        Back - index 4
    %        --------------
    %        |  d2  |  d3 |
    %        |      |     |
    % Left   --------------  Right - Index 6
    %        |  d1  |  d4 |
    % Index 5|      |     |
    %        --------------
    %         Front - Index 3
    %
    % Bottom Dice Layer = d5, d6, d7, d8 - same position as d1, d2, d3, d4
    % Array Index = [U,D,F,B,L,R] = [1,2,3,4,5,6]
    
    include "globals.mzn"; 
    int: n = 6;
    
    % composite dice face totals
    var 4..24: tot_face1;
    var 4..24: tot_face2;
    var 4..24: tot_face3;
    var 4..24: tot_face4;
    var 4..24: tot_face5;
    var 4..24: tot_face6;
    
    % Faces: U (Top), D (Bottom), L (Left), R (Right), F (Front), B (Back)
    array[1..n] of var 1..6: dice1;   % visible faces = U,L,F
    array[1..n] of var 1..6: dice2;   % visible faces = U,L,B 
    array[1..n] of var 1..6: dice3;   % visible faces = U,R,B 
    array[1..n] of var 1..6: dice4;   % visible faces = U,R,F 
    array[1..n] of var 1..6: dice5;   % visible faces = D,L,F 
    array[1..n] of var 1..6: dice6;   % visible faces = D,L,B 
    array[1..n] of var 1..6: dice7;   % visible faces = D,R,B 
    array[1..n] of var 1..6: dice8;   % visible faces = D,R,F 
    
    set of int :primes = { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
    set of int: squares = { 4, 9, 16 };
    
    % Opposite side dice sumes are equal for the eight dice
    constraint dice1[1] + dice1[2] == dice1[3]+ dice1[4] /\ dice1[1] + dice1[2] == dice1[5] + dice1[6];
    constraint dice2[1] + dice2[2] == dice2[3]+ dice2[4] /\ dice2[1] + dice2[2] == dice2[5] + dice2[6];
    constraint dice3[1] + dice3[2] == dice3[3]+ dice3[4] /\ dice3[1] + dice3[2] == dice3[5] + dice3[6];
    constraint dice4[1] + dice4[2] == dice4[3]+ dice4[4] /\ dice4[1] + dice4[2] == dice4[5] + dice4[6];
    constraint dice5[1] + dice5[2] == dice5[3]+ dice5[4] /\ dice5[1] + dice5[2] == dice5[5] + dice5[6];
    constraint dice6[1] + dice6[2] == dice6[3]+ dice6[4] /\ dice6[1] + dice6[2] == dice6[5] + dice6[6];
    constraint dice7[1] + dice7[2] == dice7[3]+ dice7[4] /\ dice7[1] + dice7[2] == dice7[5] + dice7[6];
    constraint dice8[1] + dice8[2] == dice8[3]+ dice8[4] /\ dice8[1] + dice8[2] == dice8[5] + dice8[6];
    
    % Dice standard totals for the eight dice
    constraint sum( [dice1[1] + dice1[2] + dice1[3] + dice1[4] + dice1[5] + dice1[6]]) == 21;
    constraint sum( [dice2[1] + dice2[2] + dice2[3] + dice2[4] + dice2[5] + dice2[6]]) == 21;
    constraint sum( [dice3[1] + dice3[2] + dice3[3] + dice3[4] + dice3[5] + dice3[6]]) == 21;
    constraint sum( [dice4[1] + dice4[2] + dice4[3] + dice4[4] + dice4[5] + dice4[6]]) == 21;
    constraint sum( [dice5[1] + dice5[2] + dice5[3] + dice5[4] + dice5[5] + dice5[6]]) == 21;
    constraint sum( [dice6[1] + dice6[2] + dice6[3] + dice6[4] + dice6[5] + dice6[6]]) == 21;
    constraint sum( [dice7[1] + dice7[2] + dice7[3] + dice7[4] + dice7[5] + dice7[6]]) == 21;
    constraint sum( [dice8[1] + dice8[2] + dice8[3] + dice8[4] + dice8[5] + dice8[6]]) == 21;
    
    % Array Index = [ U,D,F,B,L,R] - ie indices are [1,2,3,4,5,6]
    
    constraint all_different (dice1) /\ all_different (dice2) 
    /\ all_different (dice3) /\ all_different (dice4) /\ 
    all_different (dice5) /\ all_different (dice6)
    /\ all_different (dice7) /\ all_different (dice8);
       
    % No 4's to show on external composite dice faces 
    
    % UPPER 4-dice layer - for dice1, dice2, dice3 and dice4
    % visible faces are (U,L,F), (U,L,B), (U,R,B), (U,R,F)
    % ie indices (1,5,3), (1,5,4), (1,6,4), (1,6,3)
    constraint dice1[1] != 4 /\ dice1[5] != 4 /\ dice1[3] != 4;
    constraint dice2[1] != 4 /\ dice2[5] != 4 /\ dice2[4] != 4;
    constraint dice3[1] != 4 /\ dice3[6] != 4 /\ dice3[4] != 4;
    constraint dice4[1] != 4 /\ dice4[6] != 4 /\ dice4[3] != 4;
    
    % LOWER 4-dice layer - for dice5, dice6, dice7 and dice8
    % visible faces are (D,L,F), (D,L,B), (D,R,B), (D,R,F) 
    % ie indices (2,5,3), (2,5,4), (2,6,4), (2,6,3)
    constraint dice5[2] != 4 /\ dice5[5] != 4 /\ dice5[3] != 4;
    constraint dice6[2] != 4 /\ dice6[5] != 4 /\ dice6[4] != 4;
    constraint dice7[2] != 4 /\ dice7[6] != 4 /\ dice7[4] != 4 ;
    constraint dice8[2] != 4 /\ dice8[6] != 4 /\ dice8[3] != 4;
    
    % Composite dice - visible face total values
    constraint tot_face1 = dice1[5] + dice2[5] + dice5[5] + dice6[5];  % left face
    constraint tot_face2 = dice2[4] + dice3[4] + dice6[4] + dice7[4];  % back face
    constraint tot_face3 = dice3[6] + dice4[6] + dice7[6] + dice8[6];  % right face
    constraint tot_face4 = dice1[3] + dice4[3] + dice5[3] + dice8[3];  % front face
    constraint tot_face5 = dice1[1] + dice2[1] + dice3[1] + dice4[1];  % upper face
    constraint tot_face6 = dice5[2] + dice6[2] + dice7[2] + dice8[2];  % down face
    
    % Opposite faces of composite dice to be the same total
    constraint (tot_face1 + tot_face3 == tot_face2 + tot_face4) 
    /\ (tot_face2 + tot_face4 == tot_face5 + tot_face6);
    
    % Three of six faces of composite dice are prime numbers
    constraint sum ( [ tot_face1 in primes, tot_face2 in primes, 
    tot_face3 in primes, tot_face4 in primes, tot_face5 in primes, 
    tot_face6 in primes] ) == 3;
    
    % Three of six faces of composite dice are squares
    constraint sum ( [ tot_face1 in squares, tot_face2 in squares, tot_face3 in squares, 
    tot_face4 in squares, tot_face5 in squares, tot_face6 in squares] ) == 3;
    
    % Make totals of all faces of composite dice different
    constraint all_different([tot_face1, tot_face2, tot_face3, tot_face4, tot_face5, tot_face6]);
    
    solve satisfy;
    
    output [ "Composite visible dice face totals are: " ++ "\n" 
    ++ show( [dice1[5], dice2[5], dice5[5], dice6[5]] ) ++ "  Left face: " ++ show(tot_face1) ++ "\n"  
    ++ show( [dice2[4], dice3[4], dice6[4], dice7[4]] ) ++ "  Back face: " ++ show(tot_face2) ++ "\n"   
    ++ show( [dice3[6], dice4[6], dice7[6], dice8[6]] ) ++ "  Right face: " ++ show(tot_face3) ++ "\n"   
    ++ show( [dice1[3], dice4[3], dice5[3], dice8[3]] ) ++ "  Front face: " ++ show(tot_face4) ++ "\n" 
    ++ show( [dice1[1], dice2[1], dice3[1], dice4[1]] ) ++ "  Upper face: " ++ show(tot_face5) ++ "\n"  
    ++ show( [dice5[2], dice6[2], dice7[2], dice8[2]] ) ++ "  Down face: " ++ show(tot_face6) ++ "\n " 
     
    ++ "\n" ++ "All dice face arrangements are: " ++ "\n " ++
    "dice1 = " ++ show(dice1) ++ "  " ++ "  dice2 = " ++ show(dice2) ++ "\n " ++
    "dice3 = " ++ show(dice3) ++ "  " ++ "  dice4 = " ++ show(dice4) ++ "\n " ++
    "dice5 = " ++ show(dice5) ++ "  " ++ "  dice6 = " ++ show(dice6) ++ "\n " ++
    "dice7 = " ++ show(dice7) ++ "  " ++ "  dice8 = " ++ show(dice8) ];  
    
    % Composite visible dice face totals are: 
    % [1, 1, 2, 3]  Left face: 7
    % [3, 1, 2, 3]  Back face: 9
    % [3, 2, 5, 3]  Right face: 13
    % [3, 3, 3, 2]  Front face: 11
    % [5, 5, 5, 1]  Upper face: 16
    % [1, 1, 1, 1]  Down face: 4
     
    % All dice face arrangements are: 
    % dice1 = [5, 2, 3, 4, 1, 6]    dice2 = [5, 2, 4, 3, 1, 6]
    % dice3 = [5, 2, 6, 1, 4, 3]    dice4 = [1, 6, 3, 4, 5, 2]
    % dice5 = [6, 1, 3, 4, 2, 5]    dice6 = [6, 1, 5, 2, 3, 4]
    % dice7 = [6, 1, 4, 3, 2, 5]    dice8 = [6, 1, 2, 5, 4, 3]
    % ------------------------------
    % Finished in 85msec
    
    
  5. geoffrounce 7 July 2017 at 8:53 am

    I have tidied up the code and added the two extra constraints for the number of one-spots and two-spots, which gives a different orientation of the composite cube. Looks similar to your diagrams now, Jim

    % A Solution in MiniZinc
    %         Top Dice Layer
    %        =============== 
    %        Back - index 4
    %        --------------
    %        |  d2  |  d3 |
    %        |      |     |
    % Left   --------------  Right - Index 6
    %        |  d1  |  d4 |
    % Index 5|      |     |
    %        --------------
    %         Front - Index 3
    %
    % Bottom Dice Layer = d5, d6, d7, d8 - same position as d1, d2, d3, d4
    % Array Index = [U,D,F,B,L,R] = [1,2,3,4,5,6]
     
    include "globals.mzn"; 
    int: n = 6;
     
    % composite dice face totals
    var 4..24: tot_face1;
    var 4..24: tot_face2;
    var 4..24: tot_face3;
    var 4..24: tot_face4;
    var 4..24: tot_face5;
    var 4..24: tot_face6;
     
    % Faces: U (Top), D (Bottom), L (Left), R (Right), F (Front), B (Back)
    array[1..n] of var 1..6: dice1;   % visible faces = U,L,F
    array[1..n] of var 1..6: dice2;   % visible faces = U,L,B 
    array[1..n] of var 1..6: dice3;   % visible faces = U,R,B 
    array[1..n] of var 1..6: dice4;   % visible faces = U,R,F 
    array[1..n] of var 1..6: dice5;   % visible faces = D,L,F 
    array[1..n] of var 1..6: dice6;   % visible faces = D,L,B 
    array[1..n] of var 1..6: dice7;   % visible faces = D,R,B 
    array[1..n] of var 1..6: dice8;   % visible faces = D,R,F 
     
    set of int :primes = { 2, 3, 5, 7, 11, 13, 17, 19, 23 };
    set of int: squares = { 4, 9, 16 };
     
    % Opposite side dice sumes are equal for the eight dice
    constraint dice1[1] + dice1[2] == dice1[3]+ dice1[4] /\ 
    dice1[1] + dice1[2] == dice1[5] + dice1[6];
    
    constraint dice2[1] + dice2[2] == dice2[3]+ dice2[4] /\ 
    dice2[1] + dice2[2] == dice2[5] + dice2[6];
    
    constraint dice3[1] + dice3[2] == dice3[3]+ dice3[4] /\ 
    dice3[1] + dice3[2] == dice3[5] + dice3[6];
    
    constraint dice4[1] + dice4[2] == dice4[3]+ dice4[4] /\ 
    dice4[1] + dice4[2] == dice4[5] + dice4[6];
    
    constraint dice5[1] + dice5[2] == dice5[3]+ dice5[4] /\ 
    dice5[1] + dice5[2] == dice5[5] + dice5[6];
    
    constraint dice6[1] + dice6[2] == dice6[3]+ dice6[4] /\ 
    dice6[1] + dice6[2] == dice6[5] + dice6[6];
    
    constraint dice7[1] + dice7[2] == dice7[3]+ dice7[4] /\ 
    dice7[1] + dice7[2] == dice7[5] + dice7[6];
    
    constraint dice8[1] + dice8[2] == dice8[3]+ dice8[4] /\ 
    dice8[1] + dice8[2] == dice8[5] + dice8[6];
     
    % Dice standard totals for the eight dice
    constraint sum( [dice1[1] + dice1[2] + dice1[3] + dice1[4]
     + dice1[5] + dice1[6]]) == 21;
     
    constraint sum( [dice2[1] + dice2[2] + dice2[3] + dice2[4] 
    + dice2[5] + dice2[6]]) == 21;
    
    constraint sum( [dice3[1] + dice3[2] + dice3[3] + dice3[4
    ] + dice3[5] + dice3[6]]) == 21;
    
    constraint sum( [dice4[1] + dice4[2] + dice4[3] + dice4[4] 
    + dice4[5] + dice4[6]]) == 21;
    
    constraint sum( [dice5[1] + dice5[2] + dice5[3] + dice5[4] 
    + dice5[5] + dice5[6]]) == 21;
    
    constraint sum( [dice6[1] + dice6[2] + dice6[3] + dice6[4] 
    + dice6[5] + dice6[6]]) == 21;
    
    constraint sum( [dice7[1] + dice7[2] + dice7[3] + dice7[4] 
    + dice7[5] + dice7[6]]) == 21;
    
    constraint sum( [dice8[1] + dice8[2] + dice8[3] + dice8[4] 
    + dice8[5] + dice8[6]]) == 21;
     
    % Array Index = [ U,D,F,B,L,R] - ie indices are [1,2,3,4,5,6]
     
    constraint all_different (dice1) /\ all_different (dice2) 
    /\ all_different (dice3) /\ all_different (dice4) /\ 
    all_different (dice5) /\ all_different (dice6)
    /\ all_different (dice7) /\ all_different (dice8);
        
    % No 4's to show on external composite dice faces 
     
    % UPPER 4-dice layer - for dice1, dice2, dice3 and dice4
    % visible faces are (U,L,F), (U,L,B), (U,R,B), (U,R,F)
    % ie indices (1,5,3), (1,5,4), (1,6,4), (1,6,3)
    constraint dice1[1] != 4 /\ dice1[5] != 4 /\ dice1[3] != 4;
    constraint dice2[1] != 4 /\ dice2[5] != 4 /\ dice2[4] != 4;
    constraint dice3[1] != 4 /\ dice3[6] != 4 /\ dice3[4] != 4;
    constraint dice4[1] != 4 /\ dice4[6] != 4 /\ dice4[3] != 4;
     
    % LOWER 4-dice layer - for dice5, dice6, dice7 and dice8
    % visible faces are (D,L,F), (D,L,B), (D,R,B), (D,R,F) 
    % ie indices (2,5,3), (2,5,4), (2,6,4), (2,6,3)
    constraint dice5[2] != 4 /\ dice5[5] != 4 /\ dice5[3] != 4;
    constraint dice6[2] != 4 /\ dice6[5] != 4 /\ dice6[4] != 4;
    constraint dice7[2] != 4 /\ dice7[6] != 4 /\ dice7[4] != 4 ;
    constraint dice8[2] != 4 /\ dice8[6] != 4 /\ dice8[3] != 4;
     
    % Composite dice - visible face total values
    constraint tot_face1 = dice1[5] + dice2[5] + dice5[5] + dice6[5];  % left face
    constraint tot_face2 = dice2[4] + dice3[4] + dice6[4] + dice7[4];  % back face
    constraint tot_face3 = dice3[6] + dice4[6] + dice7[6] + dice8[6];  % right face
    constraint tot_face4 = dice1[3] + dice4[3] + dice5[3] + dice8[3];  % front face
    constraint tot_face5 = dice1[1] + dice2[1] + dice3[1] + dice4[1];  % upper face
    constraint tot_face6 = dice5[2] + dice6[2] + dice7[2] + dice8[2];  % down face
     
    % Opposite faces of composite dice to be the same total
    constraint (tot_face1 + tot_face3 == tot_face2 + tot_face4) 
    /\ (tot_face2 + tot_face4 == tot_face5 + tot_face6);
     
    % Three of six faces of composite dice are prime numbers
    constraint sum ( [ tot_face1 in primes, tot_face2 in primes, 
    tot_face3 in primes, tot_face4 in primes, tot_face5 in primes, 
    tot_face6 in primes] ) == 3;
     
    % Three of six faces of composite dice are squares
    constraint sum ( [ tot_face1 in squares, tot_face2 in squares, tot_face3 in squares, 
    tot_face4 in squares, tot_face5 in squares, tot_face6 in squares] ) == 3;
     
    % Make totals of all faces of composite dice different
    constraint all_different([tot_face1, tot_face2, tot_face3, tot_face4, 
    tot_face5, tot_face6]);
    
    % There must be 8 one-spots showing on external dice faces
    constraint sum ( [ dice1[5]=1, dice2[5]=1, dice5[5]=1, dice6[5]=1,  
                       dice2[4]=1, dice3[4]=1, dice6[4]=1, dice7[4]=1,  
                       dice3[6]=1, dice4[6]=1, dice7[6]=1, dice8[6]=1,  
                       dice1[3]=1, dice4[3]=1, dice5[3]=1, dice8[3]=1,  
                       dice1[1]=1, dice2[1]=1, dice3[1]=1, dice4[1]=1,  
                       dice5[2]=1, dice6[2]=1, dice7[2]=1, dice8[2]=1 ] ) == 8;  
                       
    % There must be 4 two-spots showing on external dice faces
    constraint sum ( [ dice1[5]=2, dice2[5]=2, dice5[5]=2, dice6[5]=2, 
                       dice2[4]=2, dice3[4]=2, dice6[4]=2, dice7[4]=2,  
                       dice3[6]=2, dice4[6]=2, dice7[6]=2, dice8[6]=2,  
                       dice1[3]=2, dice4[3]=2, dice5[3]=2, dice8[3]=2,  
                       dice1[1]=2, dice2[1]=2, dice3[1]=2, dice4[1]=2,  
                       dice5[2]=2, dice6[2]=2, dice7[2]=2, dice8[2]=2 ] ) == 4;                  
     
    solve satisfy;
     
    output [ "Composite visible dice face totals are: " ++ "\n" 
     ++ show( [dice1[5], dice2[5], dice5[5], dice6[5]] ) ++ "  Left face: " 
     ++ show(tot_face1) ++ "\n"  
     ++ show( [dice3[6], dice4[6], dice7[6], dice8[6]] ) ++ "  Right face: "
     ++ show(tot_face3) ++ "\n"  
     
     ++ show( [dice2[4], dice3[4], dice6[4], dice7[4]] ) ++ "  Back face: "
     ++ show(tot_face2) ++ "\n"    
     ++ show( [dice1[3], dice4[3], dice5[3], dice8[3]] ) ++ "  Front face: "
     ++ show(tot_face4) ++ "\n" 
     
     ++ show( [dice1[1], dice2[1], dice3[1], dice4[1]] ) ++ "  Upper face: "
     ++ show(tot_face5) ++ "\n"  
     ++ show( [dice5[2], dice6[2], dice7[2], dice8[2]] ) ++ "  Down face: " 
     ++ show(tot_face6) ]; 
      
    % Composite visible dice face totals are: 
    % [3, 3, 3, 2]  Left face: 11
    % [2, 3, 3, 1]  Right face: 9
    % [2, 3, 1, 1]  Back face: 7
    % [5, 5, 1, 2]  Front face: 13
    % [1, 1, 1, 1]  Upper face: 4
    % [5, 3, 5, 3]  Down face: 16
    % ------------------
    % Finished in 76msec
    
    

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: