0

EDITED TO UPDATE MY CODE AND GIVE FULL RESPONSE TO @MOHAMMED

Picture, if you will, a cube. Each face of that cube has a group of 4 numbers. Each number on the cube only occurs once.

So far, I have managed, with some help from here, to write a program that filters those 24 numbers into a list where every face of 4 numbers totals 50, no pair on a face total 25, but every number and it's direct opposite (physically) total 25.

The problem I now have is the corners. I have a rule I wish to apply to them. The 3 numbers that met at each corner must all total 37 or 38. There are some other rules too, but for now let's just stick with that.

Now, like a die, opposite numbers all total to the same. This means, that for all intents, each group I have had an opposite, and as such, it can never be adjacent to it.

So my program has spat its first group of 6 faces that match my criteria. How do I now work out the corners?

I have realised that I only NEED to work with 3 of the 6 faces, as with each number being paired with an opposite to total 25, once one corner matches the rules, it's opposite should do as well.

I've simplified my code to just this issue.

group1 = list(['A','B','E','F'])
group2 = list(['A','D','E','G'])
group3 = list(['A','B','C','D'])
group4 = list(['E','F','G','H'])
group5 = list(['B','C','F','H'])
group6 = list(['C','D','G','H'])

for a in group1:
  for b in group2:
    for c in group3:
      for corner1 in itertools.combinations((a,b,c),3):
        if (a == b == c):
          print (corner1)

Gives

('A','A','A')

Assuming there was no match, then there are two other possible group combinations after 1,2,3; and those are 1,3,5 and 1,4,5.

Using 1 as a constant by which all others are paired to make it a bit simpler, I think.

I have 2 issues, firstly, assuming our first match only appears in the last combination of groups, What would be the best way to write the code?

for a in group1:
  for b in group2:
    for c in group3:
      for d in group4:
        for e in group5:
          for f in group6:
            for corner1 in itertools.combinations((a,b,c),3):
          else:
            for corner1 in itertools.combinations((a,b,d),3):
          else:
            for corner1 in itertools.combinations((a,d,e),3):

Or:

for a in group1:
  for b in group2:
    for c in group3:
      for corner1 in itertools.combinations((a,b,c),3):
else for a in group1:
  for b in group2:
    for c in group4:
      for corner1 in itertools.combinations((a,b,d),3):
else for a in group1:
  for b in group2:
    for c in group3:
      for corner1 in itertools.combinations((a,d,e),3):

And even if either of those bits of code are workable (I have my doubts on both as I'm a novice), I then need to both remove those numbers from further matches with the same group AND preserve the order of the groups.

For example, if the combination 1,2,4 is chosen by the first bit of the rule, how do I make certain that subsequent query's only work if that is true (oues that make sense?)

This is my complete code so far.... EDITED

n = 0
cnr1 = 0
cnr2 = 0
cnr3 = 0
cnr4 = 0
faces_all = list(range(1,25))
for x in itertools.combinations((faces_all[1:24]),3):
  if faces_all[0] + sum(x) == 50:
    side1 = (faces_all[0], x[0], x[1], x[2])
    for pair in itertools.combinations(side1, 2):
      if sum(pair) == 25:
        break
    else:
      side6 = (25-side1[0], 25-side1[1], 25-side1[2], 25-side1[3])
      faces_remaining = list(set(faces_all) - set(side1) - set(side6))
      for y in itertools.combinations(faces_remaining,4):
        if sum(y) == 50:
          side2 = y
          for pair in itertools.combinations(side2,2):
            if sum(pair) == 25:
              break
          else:
            side5 = (25-side2[0], 25-side2[1], 25-side2[2], 25-side2[3])
            faces_last = list(set(faces_remaining) - set(side2) - set(side5))
            for z in itertools.combinations(faces_last,4):
              if sum(z) == 50:
                side3 = z
                for pair in itertools.combinations(side3,2):
                  if sum(pair) == 25:
                    break
                  else:
                    side4 = (25-side3[0], 25-side3[1], 25-side3[2], 25-side3[3])
                    for a in side2:
                      for b in side3:
                        for c in side4:
                          for d in side5:
                            for top in itertools.permutations(side1,4):
                              for corner1 in itertools.combinations((top[0],a,b),3):
                                if (sum(corner1) == 37 or sum(corner1) == 38):
                                  corner8 = (25-top[0],25-a,25-b)
                                  cnr1 += 1
                                  print ("1&8",cnr1,"2&7",cnr2,"3&6",cnr3)
                                  for corner2 in itertools.combinations((top[1],b,d),3):
                                    #if (b not in corner1 and sum(corner1) + sum(corner2) == 75):
                                    if sum(corner1) + sum(corner2) == 75:
                                      corner7 = (25-top[1],25-b,25-d)
                                      cnr2 += 1
                                      print ("1&8",cnr1,"2&7",cnr2,"3&6",cnr3)
                                      for corner3 in itertools.combinations((top[2],d,c),3):
                                        #if (d not in corner1 and sum(corner3) == sum(corner1)):
                                        if sum(corner3) == sum(corner1):
                                         corner6 = (25-top[2],25-d,25-c)
                                         cnr3 += 1
                                         print ("1&8",cnr1,"2&7",cnr2,"3&6",cnr3)
                                         for corner4 in itertools.combinations((top[2],c,a),3):
                                           #if (c not in corner3 and a not in corner1 and sum(corner4) + sum(corner1) == 75):
                                           if sum(corner4) + sum(corner1) == 75:
                                              corner5 = (25-top[2],25-c,25-a)
                                              print ("1&8",cnr1,"2&7",cnr2,"3&6",cnr3)
                                              print (n)
                                              print ("sides:", side1, side2, side3, side4, side5, side6)
                                              print ("corners:", corner1, corner2, corner3, corner4, corner5, corner6)

The output (from start to where the code stops without error) is:

1&8 1 2&7 0 3&6 0
...
1&8 8109504 2&7 213792 3&6 5558

So 8 million hits for corners 1&8, only 200,000 for 1&8 AND 2&7, and only 5,000 for 1&8, 2&7, AND 3&6 - but nothing that includes 4&5).

This suggests that either what I am trying is not possible to achieve (which is possible), or there is something wrong with my code.

Attempt at diagrams to make it clearer:

  A____________B
  |\A        B:\
  |A\         :B\
  |  \        :  \
  |   \D______:__C\C
  |  D|D      :  C|
 E|E  |      F:F  |
  \```|````````.  |
   \  |         . |
    \H|          .|
     \|H_________G|G
       H         G


          +-----+
          | E F |
          | A B |
    +-----+-----+-----+-----+
    | E A | A B | B F | F E |
    | H G | D C | C G | G H |
    +-----+-----+-----+-----+
          | D c |  
          | H G |   
          +-----+  

  +----------------------+
  |Ee                  fF|        
  |  +--+----------+--+  |
  |  |37|e   50   f|38|  |          
  |  +--+--+    +--+--+  |`
  |  |E |38|a  b|37| F|  |
  |  |  +--+----+--+  |  |
  |  |   Aa|A  B|bB   |  |
  |  | 50  | 50 |  50 |  |
  |  |   Dd|D  C|cC   |  | 
  |  |  +--+----+--+  |  |  
  |  |H |37|d  c|38| G|  |  
  |  +--+--+ 50 +--+--+  |
  |  |38|h        g|37|  |
  |  +--+----------+--+  |
  |Hh                  gG|
  +----------------------+

===========================================================================

RESPONSE to @Mohammed's code

from itertools import combinations

# Filter functions
# ------------
## All sides add to 50
def check_sides(lst):
  sides = [lst[0] + lst[1] + lst[2] + lst[3],
         lst[4] + lst[5] + lst[12] + lst[13],
         lst[6] + lst[7] + lst[14] + lst[15],
         lst[8] + lst[9] + lst[16] + lst[17],
         lst[10] + lst[11] + lst[18] + lst[19],
         lst[20] + lst[21] + lst[22] + lst[23]]
  return all(side == 50 for side in sides)

# All corners are 37 or 38
def check_corners(lst):
  print(".")
  corners = [lst[5] + lst[6] + lst[2],
         lst[3] + lst[7] + lst[8],
         lst[13] + lst[14] + lst[20],
         lst[21] + lst[15] + lst[16],
         lst[12] + lst[22] + lst[19],
         lst[23] + lst[17] + lst[6],
         lst[1] + lst[9] + lst[10],
         lst[0] + lst[4] + lst[11]]
  return all(36 < corner < 39 for corner in corners)

# All opposites add to 25
def check_opposites(lst):
  print(lst)
  opposites = [lst[0] + lst[22],
         lst[2] + lst[20],
         lst[1] + lst[23],
         lst[3] + lst[21],
         lst[5] + lst[8],
         lst[4] + lst[9],
         lst[12] + lst[17],
         lst[13] + lst[16],
         lst[7] + lst[10],
         lst[6] + lst[11],
         lst[15] + lst[18],
         lst[14] + lst[19]]
  return all(pair == 25 for pair in opposites)

# No pairs on a side add to 25
def check_pairs(lst):
  pairs = [lst[0] + lst[1], lst[2] + lst[3],
         lst[0] + lst[2], lst[1] + lst[3],
         lst[0] + lst[3], lst[1] + lst[2],
         lst[4] + lst[5], lst[12] + lst[13],
         lst[4] + lst[12], lst[5] + lst[13],
         lst[4] + lst[13], lst[5] + lst[12],
         lst[6] + lst[7], lst[14] + lst[15],
         lst[6] + lst[14], lst[7] + lst[15],
         lst[6] + lst[15], lst[7] + lst[14],
         lst[8] + lst[9], lst[16] + lst[17],
         lst[8] + lst[16], lst[9] + lst[17],
         lst[8] + lst[17], lst[9] + lst[16],
         lst[10] + lst[11], lst[18] + lst[19],
         lst[10] + lst[18], lst[11] + lst[19],
         lst[10] + lst[19], lst[11] + lst[18],
         lst[20] + lst[21], lst[22] + lst[23],
         lst[20] + lst[22], lst[21] + lst[23],
         lst[20] + lst[23], lst[21] + lst[22]]
  return all(pair != 25 for pair in pairs)


# Everything else
# ---------------
def nFilter(filters, iterable):
  for f in filters:
    iterable = filter(f, iterable)
  return iterable

candidates = combinations(range(1,50), 24)
#cubes = nFilter([check_sides, check_corners, check_opposites, check_pairs], candidates)
cubes = nFilter([check_sides],candidates)
for cube in cubes:
  print(cube)

I've added in the missing "pairs" on each face (for 4 numbers there are 6 possible combinations of pairs (AB, AC, AD, BC, BD, CD).

I rearranged the order of checks to do the ones with the least arguments first.

But I'm not understanding the output, it seems that it is just printing all combinations of 24 numbers from 1 to n without applying the filters.

Changing combinations(range(1,50) to combinations(range(1,26) yields the following output, and changing it to combinations(range(1,25) (which would then only utilise the numbers I want to use (1-24) only yields the first line of the output below.

(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)
(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)

===========================================================================

Adding more code:

Playing around I have the following 2 programs full of ugly if statements, but hopefully they are working.

So I have

face_combinations = 0
faces_all = list(range(1,25))
for side in combinations((faces_all[1:24]),3):
  if faces_all[0] + sum(side) == 50:
    if faces_all[0] + side[0] != 25:
      if faces_all[0] + side[1] != 25:
        if faces_all[0] + side[2] != 25:
          if side[0] + side[1] != 25:
            if side[0] + side[2] != 25:
              if side[1] + side[2] != 25:
                side1 = (faces_all[0], side[0], side[1], side[2])
                side6 = (25-side1[0], 25-side1[1], 25-side1[2], 25-side1[3])
                faces_remaining = list(set(faces_all) - set(side1) - set(side6))
                for side in combinations(faces_remaining[1:16],4):
                  if faces_remaining[0] + sum(side) == 50:
                    if faces_remaining[0] + side[0] != 25:
                      if faces_remaining[0] + side[1] != 25:
                        if faces_remaining[0] + side[2] != 25:
                          if side[0] + side[1] != 25:
                            if side[0] + side[2] != 25:
                              if side[1] + side[2] != 25:
                                side2 = (faces_remaining[0], side[0], side[1], side[2])
                                side5 = (25-side2[0], 25-side2[1], 25-side2[2], 25-side2[3])
                                faces_last = list(set(faces_remaining) - set(side2) - set(side5))
                                for side in combinations(faces_last[1:8],4):
                                  if faces_last[0] + sum(side) == 50:
                                    if faces_last[0] + side[0] != 25:
                                      if faces_last[0] + side[1] != 25:
                                        if faces_last[0] + side[2] != 25:
                                          if side[0] + side[1] != 25:
                                            if side[0] + side[2] != 25:
                                              if side[1] + side[2] != 25:
                                                side3 = (faces_last[0], side[0], side[1], side[2])
                                                side4 = (25-side2[0], 25-side2[1], 25-side2[2], 25-side2[3])
                                                face_combinations += 1
                                                print (face_combinations, side1, side2, side3, side4, side5, side6)

which yields:

2 (1, 4, 22, 23) (5, 6, 9, 12) (7, 8, 10, 11) (20, 19, 16, 13) (20, 19, 16, 13) (24, 21, 3, 2)
3 (1, 4, 22, 23) (5, 6, 9, 13) (7, 8, 10, 11) (20, 19, 16, 12) (20, 19, 16, 12) (24, 21, 3, 2)
4 (1, 4, 22, 23) (5, 6, 9, 14) (7, 8, 10, 12) (20, 19, 16, 11) (20, 19, 16, 11) (24, 21, 3, 2)
5 (1, 4, 22, 23) (5, 6, 11, 12) (7, 8, 9, 10) (20, 19, 14, 13) (20, 19, 14, 13) (24, 21, 3, 2)
6 (1, 4, 22, 23) (5, 6, 11, 13) (7, 8, 9, 10) (20, 19, 14, 12) (20, 19, 14, 12) (24, 21, 3, 2)
7 (1, 4, 22, 23) (5, 7, 8, 11) (6, 9, 10, 12) (20, 18, 17, 14) (20, 18, 17, 14) (24, 21, 3, 2)
8 (1, 4, 22, 23) (5, 7, 8, 12) (6, 9, 10, 11) (20, 18, 17, 13) (20, 18, 17, 13) (24, 21, 3, 2)
9 (1, 4, 22, 23) (5, 7, 8, 13) (6, 9, 10, 11) (20, 18, 17, 12) (20, 18, 17, 12) (24, 21, 3, 2)
10 (1, 4, 22, 23) (5, 7, 8, 14) (6, 9, 10, 12) (20, 18, 17, 11) (20, 18, 17, 11) (24, 21, 3, 2)
...
691 (1, 15, 16, 18) (2, 3, 5, 19) (4, 8, 11, 13) (23, 22, 20, 6) (23, 22, 20, 6) (24, 10, 9, 7)
692 (1, 15, 16, 18) (2, 4, 8, 13) (3, 5, 6, 14) (23, 21, 17, 12) (23, 21, 17, 12) (24, 10, 9, 7)
693 (1, 15, 16, 18) (2, 4, 11, 12) (3, 5, 6, 17) (23, 21, 14, 13) (23, 21, 14, 13) (24, 10, 9, 7)
694 (1, 15, 16, 18) (2, 4, 11, 13) (3, 5, 6, 17) (23, 21, 14, 12) (23, 21, 14, 12) (24, 10, 9, 7)
695 (1, 15, 16, 18) (2, 4, 13, 14) (3, 5, 6, 17) (23, 21, 12, 11) (23, 21, 12, 11) (24, 10, 9, 7)
696 (1, 15, 16, 18) (2, 5, 6, 14) (3, 4, 8, 13) (23, 20, 19, 11) (23, 20, 19, 11) (24, 10, 9, 7)
697 (1, 15, 16, 18) (2, 6, 11, 12) (3, 4, 5, 17) (23, 19, 14, 13) (23, 19, 14, 13) (24, 10, 9, 7)

And this:

numbers_all = list(range(1,25))
corner_combinations = 0
for corner in combinations((numbers_all[1:24]), 2):
  if 36 < numbers_all[0] + sum(corner) < 39:
    corner1 = (numbers_all[0], corner[0], corner[1])
    if corner1[0] + corner1[1] != 25:
      if corner1[0] + corner1[2] != 25:
        if corner1[1] + corner1[2] != 25:
          corner8 = (25 - corner1[0], 25 - corner1[1], 25 - corner1[2])
          numbers_remaining = list(set(numbers_all) - set(corner1) - set(corner8))
          for corner in combinations((numbers_remaining[1:18]), 2):
            if 36 < numbers_remaining[0] + sum(corner) < 39:
              corner2 = (numbers_remaining[0], corner[0], corner[1])
              if sum(corner1) + sum(corner2) ==75:
                if corner2[0] + corner2[1] != 25:
                  if corner2[0] + corner2[2] != 25:
                    if corner2[1] + corner2[2] != 25:
                      corner7 = (25 - corner2[0], 25 - corner2[1], 25 - corner2[2])
                      numbers_remaining2 = list(set(numbers_remaining) - set(corner2) - set(corner7))
                      for corner in combinations((numbers_remaining2[1:12]), 2):
                        if 36 < numbers_remaining2[0] + sum(corner) < 39:
                          corner3 = (numbers_remaining2[0], corner[0], corner[1])
                          if sum(corner3) == sum(corner1):
                            if corner3[0] + corner3[1] != 25:
                              if corner3[0] + corner3[2] != 25:
                                if corner3[1] + corner3[2] != 25:
                                  corner6 = (25 - corner3[0], 25 - corner3[1], 25 - corner3[2])
                                  numbers_remaining3 = list(set(numbers_remaining2) - set(corner3) - set(corner6))
                                  for corner in combinations((numbers_remaining3[1:6]), 2):
                                    if 36 < numbers_remaining3[0] + sum(corner) < 39:
                                      corner4 = (numbers_remaining3[0], corner[0], corner[1])
                                      if sum(corner1) + sum(corner4) ==75:
                                        if corner4[0] + corner4[1] != 25:
                                          if corner4[0] + corner4[1] != 25:
                                            if corner4[1] + corner4[2] != 25:
                                              corner5 = (25 - corner4[0], 25 - corner4[1], 25 - corner4[2])
                                              corner_combinations += 1
                                              print (corner_combinations, corner1, corner2, corner3, corner4, corner5, corner6, corner7, corner8)

Yields this:

2 (1, 14, 22) (2, 16, 20) (4, 15, 18) (6, 13, 19) (19, 12, 6) (21, 10, 7) (23, 9, 5) (24, 11, 3)
3 (1, 14, 22) (2, 17, 19) (4, 13, 20) (7, 15, 16) (18, 10, 9) (21, 12, 5) (23, 8, 6) (24, 11, 3)
4 (1, 14, 22) (2, 17, 19) (4, 15, 18) (5, 13, 20) (20, 12, 5) (21, 10, 7) (23, 8, 6) (24, 11, 3)
5 (1, 14, 23) (3, 15, 19) (4, 16, 18) (5, 12, 20) (20, 13, 5) (21, 9, 7) (22, 10, 6) (24, 11, 2)
6 (1, 14, 23) (3, 16, 18) (4, 15, 19) (5, 12, 20) (20, 13, 5) (21, 10, 6) (22, 9, 7) (24, 11, 2)
7 (1, 15, 21) (2, 17, 19) (3, 14, 20) (7, 13, 18) (18, 12, 7) (22, 11, 5) (23, 8, 6) (24, 10, 4)
8 (1, 15, 21) (2, 17, 19) (3, 16, 18) (5, 13, 20) (20, 12, 5) (22, 9, 7) (23, 8, 6) (24, 10, 4)
9 (1, 15, 22) (2, 14, 21) (5, 16, 17) (6, 12, 19) (19, 13, 6) (20, 9, 8) (23, 11, 4) (24, 10, 3)
10 (1, 15, 22) (2, 14, 21) (5, 16, 17) (6, 13, 18) (19, 12, 7) (20, 9, 8) (23, 11, 4) (24, 10, 3)
11 (1, 15, 22) (2, 16, 19) (4, 14, 20) (7, 12, 18) (18, 13, 7) (21, 11, 5) (23, 9, 6) (24, 10, 3)
12 (1, 15, 22) (2, 16, 19) (4, 14, 20) (7, 13, 17) (18, 12, 8) (21, 11, 5) (23, 9, 6) (24, 10, 3)
13 (1, 15, 22) (2, 17, 18) (4, 14, 20) (6, 12, 19) (19, 13, 6) (21, 11, 5) (23, 8, 7) (24, 10, 3)
14 (1, 16, 20) (2, 14, 22) (4, 15, 18) (6, 13, 19) (19, 12, 6) (21, 10, 7) (23, 11, 3) (24, 9, 5)
15 (1, 16, 21) (2, 13, 22) (5, 15, 18) (6, 14, 17) (19, 11, 8) (20, 10, 7) (23, 12, 3) (24, 9, 4)
16 (1, 16, 21) (2, 15, 20) (3, 17, 18) (6, 12, 19) (19, 13, 6) (22, 8, 7) (23, 10, 5) (24, 9, 4)
17 (1, 16, 21) (2, 17, 18) (3, 15, 20) (6, 12, 19) (19, 13, 6) (22, 10, 5) (23, 8, 7) (24, 9, 4)
18 (1, 17, 19) (2, 14, 22) (4, 13, 20) (7, 15, 16) (18, 10, 9) (21, 12, 5) (23, 11, 3) (24, 8, 6)
19 (1, 17, 19) (2, 14, 22) (4, 15, 18) (5, 13, 20) (20, 12, 5) (21, 10, 7) (23, 11, 3) (24, 8, 6)
20 (1, 17, 19) (2, 15, 21) (3, 14, 20) (7, 13, 18) (18, 12, 7) (22, 11, 5) (23, 10, 4) (24, 8, 6)
21 (1, 17, 19) (2, 15, 21) (3, 16, 18) (5, 13, 20) (20, 12, 5) (22, 9, 7) (23, 10, 4) (24, 8, 6)
22 (1, 17, 20) (2, 13, 22) (4, 15, 19) (7, 14, 16) (18, 11, 9) (21, 10, 6) (23, 12, 3) (24, 8, 5)
23 (1, 17, 20) (2, 14, 21) (3, 16, 19) (7, 12, 18) (18, 13, 7) (22, 9, 6) (23, 11, 4) (24, 8, 5)
24 (1, 17, 20) (2, 16, 19) (3, 14, 21) (7, 12, 18) (18, 13, 7) (22, 11, 4) (23, 9, 6) (24, 8, 5)
25 (1, 18, 19) (2, 14, 21) (3, 15, 20) (8, 12, 17) (17, 13, 8) (22, 10, 5) (23, 11, 4) (24, 7, 6)
26 (1, 18, 19) (2, 14, 21) (3, 15, 20) (8, 13, 16) (17, 12, 9) (22, 10, 5) (23, 11, 4) (24, 7, 6)
27 (1, 18, 19) (2, 15, 20) (3, 14, 21) (8, 12, 17) (17, 13, 8) (22, 11, 4) (23, 10, 5) (24, 7, 6)
28 (1, 18, 19) (2, 15, 20) (3, 14, 21) (8, 13, 16) (17, 12, 9) (22, 11, 4) (23, 10, 5) (24, 7, 6)

So 697 side combinations but only 28 corner combinations.

All I need to do now is to check one against the other somehow. Problem is the position of each side or corner is moveable.

wovano
  • 4,543
  • 5
  • 22
  • 49
Escribblings
  • 739
  • 5
  • 9
  • 17
  • You haven't clearly described how these numbers are arranged on the cube. Apparently there are 24 numbers total, but beyond that, it's not clear where any of these numbers are or how they're associated with faces or corners. – user2357112 Apr 19 '19 at 22:30
  • 4 numbers to each face. Those 4 numbers equal 50, but no pair on the same face equals 25. And each numbers direct physical opposite totals to 25. That puts 3 numbers in each corner. They should total 37 or 38. The number adjacent asking the edge in each direction should total 75, as should the number diagonally opposite through the cube. Opposite corners on each face should be equal to each other. – Escribblings Apr 19 '19 at 23:55

3 Answers3

1

I approached your problem differently, but you can adapt aspects of it for your own use. Hopefully this qualifies as an answer here since it should solve the problem, but I don't know how to find a cube that meets your conditions except by brute force. If you have some candidates already, you can modify the code slightly to only work through those.

In general the code works like a sieve. I chose to use filter() to do the heavy lifting instead of nested for loops. The advantages of this approach are that it's easier to reason through the code, you can create a new rule by writing a filter function, and you can easily re-order the filters later to improve performance.

For the code to make sense, you'll need to consult this map. Each candidate combination is a tuple with 24 elements, so the indexes go from 0 to 23. A = 0, B = 1, C = 2, ..., X = 23. I've double-checked that all the filter functions use the right indexes, but you might want to double-check.

Edit: I've corrected the pairs function with your fixes, and changed combinations to permutations. Since each number maps to a specific place on the cube, order matters. That's why combinations(range(1,25), 24) only produces one combination. There is only one unordered set of 24 numbers that contains the numbers 1 to 24. I also created the filter chain manually this time since the chaining function doesn't seem to work for this case.

          +-----+
          | A B |
          | C D |
    +-----+-----+-----+-----+
    | E F | G H | I J | K L |
    | M N | O P | Q R | S T |
    +-----+-----+-----+-----+
          | U V |
          | W X |
          +-----+
from itertools import permutations

# Filter functions
# ------------

# All corners are 37 or 38
def check_corners(lst):
    print(".")
    corners = [lst[5] + lst[6] + lst[2],
               lst[3] + lst[7] + lst[8],
               lst[13] + lst[14] + lst[20],
               lst[21] + lst[15] + lst[16],
               lst[12] + lst[22] + lst[19],
               lst[23] + lst[17] + lst[6],
               lst[1] + lst[9] + lst[10],
               lst[0] + lst[4] + lst[11]]
    return all(36 < corner < 39 for corner in corners)

# All sides add to 50
def check_sides(lst):
    sides = [lst[0] + lst[1] + lst[2] + lst[3],
             lst[4] + lst[5] + lst[12] + lst[13],
             lst[6] + lst[7] + lst[14] + lst[15],
             lst[8] + lst[9] + lst[16] + lst[17],
             lst[10] + lst[11] + lst[18] + lst[19],
             lst[20] + lst[21] + lst[22] + lst[23]]
    return all(side == 50 for side in sides)

# All opposites add to 25
def check_opposites(lst):
    print(lst)
    opposites = [lst[0] + lst[22],
                 lst[2] + lst[20],
                 lst[1] + lst[23],
                 lst[3] + lst[21],
                 lst[5] + lst[8],
                 lst[4] + lst[9],
                 lst[12] + lst[17],
                 lst[13] + lst[16],
                 lst[7] + lst[10],
                 lst[6] + lst[11],
                 lst[15] + lst[18],
                 lst[14] + lst[19]]
    return all(pair == 25 for pair in opposites)

# No pairs on a side add to 25
def check_pairs(lst):
    pairs = [lst[0] + lst[1], lst[2] + lst[3],
             lst[0] + lst[2], lst[1] + lst[3],
             lst[0] + lst[3], lst[1] + lst[2],
             lst[4] + lst[5], lst[12] + lst[13],
             lst[4] + lst[12], lst[5] + lst[13],
             lst[4] + lst[13], lst[5] + lst[12],
             lst[6] + lst[7], lst[14] + lst[15],
             lst[6] + lst[14], lst[7] + lst[15],
             lst[6] + lst[15], lst[7] + lst[14],
             lst[8] + lst[9], lst[16] + lst[17],
             lst[8] + lst[16], lst[9] + lst[17],
             lst[8] + lst[17], lst[9] + lst[16],
             lst[10] + lst[11], lst[18] + lst[19],
             lst[10] + lst[18], lst[11] + lst[19],
             lst[10] + lst[19], lst[11] + lst[18],
             lst[20] + lst[21], lst[22] + lst[23],
             lst[20] + lst[22], lst[21] + lst[23],
             lst[20] + lst[23], lst[21] + lst[22]]
    return all(pair != 25 for pair in pairs)

candidates = permutations(range(1,25), 24)
cubes = filter(check_pairs,
               filter(check_corners,
                      filter(check_corners,
                             filter(check_sides, candidates))))
for cube in cubes:
    print(cube)

What this code does:

  1. permutations produces an iterator of tuples of 24 integers from 1 to 24, in all the possible orderings. This means it will act like a list of possible candidate cubes, but only creates each tuple when it is asked rather than beforehand. This limits memory usage. Each combination is like (n1, n2, n3, ... n24). Each member of this tuple corresponds to one of the numbers on your cube.
  2. The chained filter works like a sieve. It starts from the innermost filter and works its way out. Each inner filter passes on only those permutations that meet all the conditions of the filtering function.
  3. What all() does is return True if all the comparisons inside the parentheses are True. The expression inside the parentheses is called a generator expression.
  4. cubes contains a chained iterator that checks conditions in the order that you see in the list. You can speed up performance if you're certain about which ones eliminate the most candidate cubes. The more candidates you eliminate at the beginning of the chain, the less time wasted on them down the line.
  5. The for loop is the first time any of the checking code is run, because when the loop asks cubes for an item, cubes has to ask filter(check_pairs, ...) which has to ask filter(check_sides, ...) all the way down. So, check_sides is the deepest level, meaning it is the first comparison that actually gets performed.
  6. Each time cubes spits out a tuple with values that meet all the conditions specified by the filter functions, the for loop prints it out.

Lastly, if you keep track of how many combinations you have already tried and the program crashes or you have to pause, check here for an example of how to skip to the nth item of an iterable, which is combinations() in this case.

Mohammed
  • 11
  • 2
  • I had a quick look at this last night, but am going to take another look now. You've missed some pairs in the pairs filter - `lst[0] + lst[1], lst[2] + lst[3], lst[0] + lst[2], lst[1] + lst[3],` should have a 3rd line: `lst[0] + lst[3], lst[1] lst[4]` – Escribblings Apr 21 '19 at 09:16
  • Correction to last comment - too late to edit: I had a quick look at this last night, but am going to take another look now. You've missed some pairs in the pairs filter for example - `lst[0] + lst[1], lst[2] + lst[3], lst[0] + lst[2], lst[1] + lst[3],` should have a 3rd line: `lst[0] + lst[3], lst[1] lst[2]` – Escribblings Apr 21 '19 at 09:26
  • I've changed the post to add your fix to the pair-checking function, and used permutations instead of combinations since I realized that's what you really want. – Mohammed Apr 21 '19 at 16:46
  • Thanks, but I don't think that will work.!24 permutations will likely take thousands of years to complete! – Escribblings Apr 21 '19 at 23:14
0

Adding this as an answer as opposed to editing the question once again, just for clarity.

So using my corner and side code, I've run a list comparison on the 2 lists I've created, the corners and the sides.

Code now appears to work. It's messy, but it works (I think)

Thank you for your help.

from itertools import combinations

# program to distribute 24 numbers across the 6 sides of a cube
# there will be 4 numbers to a side
# and 3 numbers at each corner where 3 sides meet
#  every number and its physical opposite must sum to 25.
# this means only the 1st 4 corners and 3 sides have to be worked out.  
# as the opposites will automatically obey the rules. 
# no pairs of numbers in a corner or on a side may equal 25
# the sum of a side must equal 50
# the sum of a corner must equal 37 or 38
# the sum of 2 corners connected by an edge must equal 75
# corners opposite each other on a side must be equal

corner_combinations = 0 # a simple counter
side_combinations = 0 # a simple counter
side_numbers_all = list(range(1,25)) # list of all numbers from 1 to 24
corner_numbers_all = list(range(1,25)) # list of all numbers from 1 to 24

# find the first corner 

for corner_a in combinations((corner_numbers_all[1:24]), 2): # find combinations of 2 numbers from 2 to 23 (not using 1 or 24 as 1 is the first constant, and 24 is its direct opposite)


  if 36 < corner_numbers_all[0] + sum(corner_a) < 39: # check if the corner using 1 (the constant mentiined above) and the pair of numbers from the combination sum to 37 or 38
    corner1 = (corner_numbers_all[0], corner_a[0], corner_a[1]) # create a new corner list with the constant and the combination
    if 25 not in (corner1[0] + corner1[1], corner1[0] + corner1[2], corner1[1] + corner1[2]): # check that all possible pairings in the new list do not sum to 25
      corner8 = (25 - corner1[0], 25 - corner1[1], 25 - corner1[2]) # create a new corner list that is the opposite of the current list.

      corner_numbers_remaining = list(set(corner_numbers_all) - set(corner1) - set(corner8)) # create a new list of numbers from the 1st list minus the 2 corner lists just created

# find the second corner, rules as above
      for corner_b in combinations((corner_numbers_remaining[1:18]), 2):
        if 36 < corner_numbers_remaining[0] + sum(corner_b) < 39:
          corner2 = (corner_numbers_remaining[0], corner_b[0], corner_b[1])
          if sum(corner1) + sum(corner2) == 75: # checks to see if the sum of the first and second corners is 75
            if 25 not in (corner2[0] + corner2[1], corner2[0] + corner2[2], corner2[1] + corner2[2]):
              corner7 = (25 - corner2[0], 25 - corner2[1], 25 - corner2[2])

              corner_numbers_remaining2 = list(set(corner_numbers_remaining) - set(corner2) - set(corner7))

# find third corner, as abive
              for corner_c in combinations((corner_numbers_remaining2[1:12]), 2):
                if 36 < corner_numbers_remaining2[0] + sum(corner_c) < 39:
                  corner3 = (corner_numbers_remaining2[0], corner_c[0], corner_c[1])
                  if sum(corner3) == sum(corner1): # check to see if the sum of the first corner is the same as the sum of the third corner
                    if (corner3[0] + corner3[1] and corner3[0] + corner3[2] and corner3[1] + corner3[2]) != 25:
                      corner6 = (25 - corner3[0], 25 - corner3[1], 25 - corner3[2])

                      corner_numbers_remaining3 = list(set(corner_numbers_remaining2) - set(corner3) - set(corner6))

# find fourth corner, as per second corner
                      for corner_d in combinations((corner_numbers_remaining3[1:6]), 2):
                        if 36 < corner_numbers_remaining3[0] + sum(corner_d) < 39:
                          corner4 = (corner_numbers_remaining3[0], corner_d[0], corner_d[1])
                          if sum(corner1) + sum(corner4) ==75:
                            if (corner4[0] + corner4[1] and corner4[0] + corner4[1] and corner4[1] + corner4[2]) != 25:
                              corner5 = (25 - corner4[0], 25 - corner4[1], 25 - corner4[2])

                              corner_combinations += 1 # count how many combinations of corners meet the criteria.

# find first side, as per first corner

                              for side_a in combinations((side_numbers_all[1:24]),3): # 3 number combination this time, still dropping 1 and 24
                                if side_numbers_all[0] + sum(side_a) == 50: # check constant and combination sum to 50
                                  if 25 not in (side_numbers_all[0] + side_a[0], side_numbers_all[0] + side_a[1], side_numbers_all[0] + side_a[2], side_a[0] + side_a[1], side_a[0] + side_a[2], side_a[1] + side_a[2]): # check no pairs of numbers sum to 25
                                    side1 = (side_numbers_all[0], side_a[0], side_a[1], side_a[2]) # createvfirst side list
                                    if len(set(side1).intersection(corner1)) <= 1 and len(set(side1).intersection(corner2)) <= 1 and len(set(side1).intersection(corner3)) <= 1 and len(set(side1).intersection(corner4)) <= 1 and len(set(side1).intersection(corner5)) <= 1 and len(set(side1).intersection(corner6)) <= 1 and len(set(side1).intersection(corner7)) <= 1 and len(set(side1).intersection(corner8)) <= 1: # check first side against all corners.  Each number in side may only exist in one corner.
                                      side6 = (25-side1[0], 25-side1[1], 25-side1[2], 25-side1[3]) # create the opposite side

# find second side as above
                                      side_numbers_remaining = list(set(side_numbers_all) - set(side1) - set(side6))
                                      for side_b in combinations(side_numbers_remaining[1:16],3):
                                        if side_numbers_remaining[0] + sum(side_b) == 50:
                                          if 25 not in (side_numbers_remaining[0] + side_b[0], side_numbers_remaining[0] + side_b[1], side_numbers_remaining[0] + side_b[2], side_b[0] + side_b[1], side_b[0] + side_b[2], side_b[1] + side_b[2]):
                                            side2 = (side_numbers_remaining[0], side_b[0], side_b[1], side_b[2])
                                            if len(set(side2).intersection(corner1)) <= 1 and len(set(side2).intersection(corner2)) <= 1 and len(set(side2).intersection(corner3)) <= 1 and len(set(side2).intersection(corner4)) <= 1 and len(set(side2).intersection(corner5)) <= 1 and len(set(side2).intersection(corner6)) <= 1 and len(set(side2).intersection(corner7)) <= 1 and len(set(side2).intersection(corner8)) <= 1:
                                              side5 = (25-side2[0], 25-side2[1], 25-side2[2], 25-side2[3])
                                              side_numbers_remaining2 = list(set(side_numbers_remaining) - set(side2) - set(side5))
                                              for side_c in combinations(side_numbers_remaining2[1:8],3):
                                                if side_numbers_remaining2[0] + sum(side_c) == 50:
                                                  if 25 not in (side_numbers_remaining2[0] + side_c[0], side_numbers_remaining2[0] + side_c[1], side_numbers_remaining2[0] + side_c[2], side_c[0] + side_c[1], side_c[0] + side_c[2], side_c[1] + side_c[2]):
                                                    side3 = (side_numbers_remaining2[0], side_c[0], side_c[1], side_c[2])
                                                    if len(set(side3).intersection(corner1)) <= 1 and len(set(side3).intersection(corner2)) <= 1 and len(set(side3).intersection(corner3)) <= 1 and len(set(side3).intersection(corner4)) <= 1 and len(set(side3).intersection(corner5)) <= 1 and len(set(side3).intersection(corner6)) <= 1 and len(set(side3).intersection(corner7)) <= 1 and len(set(side3).intersection(corner8)) <= 1:
                                                      side4 = (25-side3[0], 25-side3[1], 25-side3[2], 25-side3[3])

                                                      side_combinations += 1 # count how many sides combinations meet the criteria 

# print result
                                                      print ("corner combinations:", corner_combinations)
                                                      print (corner1,"=", sum(corner1), corner2,"=", sum(corner2), corner3,"=", sum(corner3), corner4,"=", sum(corner4), corner5,"=", sum(corner5), corner6,"=", sum(corner6), corner7,"=", sum(corner7), corner8,"=", sum(corner8))
                                                      print ("side combinations:", side_combinations)
                                                      print (side1,"=", sum(side1), side2,"=", sum(side2), side3,"=", sum(side3), side4,"=", sum(side4), side5,"=", sum(side5), side6,"=", sum(side6))                                              
Escribblings
  • 739
  • 5
  • 9
  • 17
0

I've just started back at this project - I had forgotten all about the above until I recovered my SOF account.

What I have created in the meantime is, I think, a brute force attempt to crack this. The code took around 5-10 minutes to complete and yielded ONE combination.

Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
>>> 
====================== RESTART: C:\Users\escri\face1.py ======================
1: [1, 12, 16, 21][18, 10, 3, 19][11, 17, 20, 2][23, 5, 8, 14][6, 22, 15, 7][4, 9, 13, 24]
There are 1 combinations
>>> 

The code is as follows:

r = range(2, 23)   # +-------------+-------------+-------------+
n = 0              # | C6:2--------| F2:0   F2:1 |--------C5:2 |
                   # | | F6:1      | C6:1   C5:1 |      F6:0 | |
f1 = [1,0,0,0]     # | |           |             |           | |
f2 = [0,0,0,0]     # | |           | F2:3   F2:2 |           | |
f3 = [0,0,0,0]     # | |           | C1:1   C2:1 |           | |
f4 = [0,0,0,0]     # +-------------+-------------+-------------+
f5 = [0,0,0,0]     # | F3:0   F3:1 | F1:0   F1:1 | F4:0   F4:1 |
f6 = [0,0,0,24]    # | C6:0   C1:2 | C1:0   C2:0 | C2:2   C5:0 |
                   # |             |             |             |
c1 = [1,0,0]       # | F3:3   F3:2 | F1:3   F1:2 | F4:3   F4:2 |
c2 = [0,0,0]       # | C7:0   C4:2 | C4:0   C3:0 | C3:2   C8:0 |
c3 = [0,0,0]       # +-------------+-------------+-------------+
c4 = [0,0,0]       # | |           | F5:2   F5:3 |           | |
c5 = [0,0,0]       # | |           | C4:1   C3:1 |           | |
c6 = [0,0,0]       # | |           |             |           | |
c7 = [0,0,0]       # | | F6:2      | F5:1   F5:0 |      F6:3 | |
c8 = [0,0,24]      # | C7:2--------| C7:1   C8:1 |--------C8:2 |
                   # +-------------+-------------+-------------+

for xa in r:
  f1[1] = xa
  f6[2] = 25 - xa
  for ya in r:
    if ya != xa and ya + xa != 25:
      f1[2] = ya
      f6[1] = 25 - ya
      for za in r:
        if za != xa and za + xa != 25 and za != ya and za + ya != 25:
          f1[3] = za
          f6[0] = 25 - za
          
          if sum(f1) == 50:
            c2[0] = f1[1]
            c7[2] = f6[2]
            c3[0] = f1[2]
            c6[2] = f6[1]
            c4[0] = f1[3]
            c5[2] = f6[0]
            
            for wb in r:
              if wb not in f1 and wb not in f6:
                f2[0] = wb
                f5[3] = 25 - wb
                for xb in r:
                  if xb not in f1 and xb not in f6 and xb != wb and xb + wb != 25:
                    f2[1] = xb
                    f5[2] = 25 - xb
                    for yb in r:
                      if yb not in f1 and yb not in f6 and yb != wb and yb + wb !=25 and yb != xb and yb + xb != 25:
                        f2[2] = yb
                        f5[1] = 25 - yb
                        for zb in r:
                          if zb not in f1 and zb not in f6 and zb != wb and zb + wb !=25 and zb != xb and zb + xb != 25 and zb != yb and zb + yb != 25:
                            f2[3] = zb
                            f5[0] = 25 - zb
                            
                            if sum(f2) == 50:
                              c6[1] = f2[0]
                              c3[1] = f5[3]
                              c5[1] = f2[1]
                              c4[1] = f5[2]
                              c2[1] = f2[2]
                              c7[1] = f5[1]
                              c1[1] = f2[3]
                              c8[1] = f5[0]
                              
                              for wc in r:
                                if wc not in f1 and wc not in f2 and wc not in f5 and wc not in f6:
                                  f3[0] = wc
                                  f4[3] = 25 - wc
                                  for xc in r:
                                    if xc not in f1 and xc not in f2 and xc not in f5 and xc not in f6 and xc != wc and xc + wc != 25:
                                      f3[1] = xc
                                      f4[2] = 25 - xc
                                      for yc in r:
                                        if yc not in f1 and yc not in f2 and yc not in f5 and yc not in f6 and yc != wc and yc + wc != 25 and yc != xc and yc + xc != 25:
                                          f3[2] = yc
                                          f4[1] = 25 - yc
                                          for zc in r:
                                            if zc not in f1 and zc not in f2 and zc not in f5 and zc not in f6 and zc != wc and zc + wc != 25 and zc != xc and zc + xc != 25 and zc != yc and zc + yc != 25:
                                              f3[3] = zc
                                              f4[0] = 25 - zc
                                              
                                              if sum(f3) == 50:
                                                c6[0] = f3[0]
                                                c3[2] = f4[3]
                                                c1[2] = f3[1]
                                                c8[0] = f4[2]
                                                c7[0] = f3[2]
                                                c5[0] = f4[1]
                                                c4[2] = f3[3]
                                                c2[2] = f4[0]
                                                
                                                if sum(c1) == 37 or sum(c1) == 38:
                                                  if sum(c1) + sum(c2) == 75 and sum(c1) + sum(c4) == 75 and sum(c1) == sum(c3) and sum(c1) + sum(c8) == 75:
                                                    n += 1
                                                    print(f"{n}: {f1}{f2}{f3}{f4}{f5}{f6}")
print(f"There are {n} combinations")
Escribblings
  • 739
  • 5
  • 9
  • 17