And now for something completely different....

This may or may not help, but here’s an example of spinning 4 disks looking for a match at the four green compare positions. Each time this is run, the 4 disks will have different sprite positions. The disks are not spinning when the program starts, tap the screen and disk 4 will spin at full speed. You can open the print window and move the slider to slow it down. The disks will stop when the sprite pairs match in the green rectangles. Tap the screen to continue again. Sometimes there won’t be any match’s, sometimes 1, sometimes a few. The number in the center shows the rotation number for disks 1 thru 4. The numbers will go from 1111 to 8888. It will stop at 8888. To solve @Bri_G challenge, no graphics need to be used. My program to solve his challenge uses no graphics and just prints out the 4 digit rotation number. This program only uses one sided disks and one image to compare, his uses 2 sided disks and uses 2 pictures to compare.

displayMode(FULLSCREEN)

function setup()
    parameter.integer("delay",0,60,0)
    pic={"Planet Cute:Character Princess Girl","Planet Cute:Character Boy","Planet Cute:Character Cat Girl","Planet Cute:Character Horn Girl","Planet Cute:Character Pink Girl","Planet Cute:Star","Planet Cute:Heart","Planet Cute:Key" }
    fill(255)
    disk={}
    r={}
    for z=1,4 do
        u={1,2,3,4,5,6,7,8}
        disk[z]={}
        r[z]={}
        for y=1,8 do
            h=math.random(#u)
            disk[z][y]=u[h] 
            table.remove(u,h)
            r[z][y]=disk[z][y] 
        end
    end
    p={1,1,1,1}
    cnt=0
    str=""
    found=true
end

function draw()
    background(224, 198, 134, 255)
    fill(255)
    ellipse(WIDTH/2-200,HEIGHT-220,370)
    ellipse(WIDTH/2+200,HEIGHT-220,370)
    ellipse(WIDTH/2-200,HEIGHT-600,370)
    ellipse(WIDTH/2+200,HEIGHT-600,370)
    fill(0)
    ellipse(WIDTH/2-200,HEIGHT-220,200)
    ellipse(WIDTH/2+200,HEIGHT-220,200)
    ellipse(WIDTH/2-200,HEIGHT-600,200)
    ellipse(WIDTH/2+200,HEIGHT-600,200)
    fill(0, 255, 93, 255)
    rect(WIDTH/2-100,HEIGHT-260,200,80)
    rect(WIDTH/2-100,HEIGHT-640,200,80)
    rect(WIDTH/2-240,HEIGHT-500,80,180)
    rect(WIDTH/2+160,HEIGHT-500,80,180)
    show(1,WIDTH/2-200,HEIGHT-70)
    show(2,WIDTH/2+200,HEIGHT-70)
    show(3,WIDTH/2-200,HEIGHT-450)
    show(4,WIDTH/2+200,HEIGHT-450)
    fill(255,0,0)
    text("Disk 1",WIDTH/2-200,HEIGHT-220)
    text("Disk 2",WIDTH/2+200,HEIGHT-220)
    text("Disk 3",WIDTH/2-200,HEIGHT-600)
    text("Disk 4",WIDTH/2+200,HEIGHT-600)
    cnt=cnt+1
    if cnt>delay then
        add()
        cnt=0
    end
    if done then
        str="search complete, press restart icon"
    else
        str=string.format("%2d %2d %2d %2d",p[1],p[2],p[3],p[4])
    end
    text(str,WIDTH/2,HEIGHT-400)
    text("tap screen to pause or continue",WIDTH/2,50)
end

function touched(t)
    if t.state==BEGAN then
        found=not found
    end
end

function show(d,x,y)
    sprite(pic[r[d][5]],x,y,80)
    sprite(pic[r[d][6]],x-100,y-50,80)
    sprite(pic[r[d][4]],x+100,y-50,80)
    sprite(pic[r[d][7]],x-140,y-140,80)
    sprite(pic[r[d][3]],x+140,y-140,80)
    sprite(pic[r[d][8]],x-100,y-230,80)
    sprite(pic[r[d][2]],x+100,y-230,80)
    sprite(pic[r[d][1]],x,y-280,80)
end

function add()
    if found or done then
        return
    end
    for a=4,1,-1 do
        p[a]=p[a]+1
        if p[a]>8 then
            if a==1 then
                done=true
            end
            p[a]=1
        else
            break
        end
    end
    for x=1,4 do
        s=p[x]
        for y=1,8 do
            r[x][y]=disk[x][s]
            s=s+1
            if s>8 then
                s=1
            end
        end
    end
    if r[1][1]==r[3][5] and r[1][3]==r[2][7] and r[2][1]==r[4][5] and r[4][7]==r[3][3] then
        found=true
    end
end

All,
Anyone out there trying to program a solution? Like to know - give me some idea on when to publish solution.

@dave1707 - thanks for the timer util, never have thought of using socket. Does that vary with your internet server etc? My solution did it in 0.88 secs - stripped out the sprites I use and it dropped to 0.87 sec!!! I also have a few variables and build print statements that could be eliminated to tidy up but I can’t see me getting much lower. How many lines of code are you using?

@Bri_G The socket code is pulled into Codea with the require, so gettime is a function call just like anything else. Not sure if it slows down if the FPS gets really low. I’ll have to try that.

My code is 75 lines.

PS. I was able to optimize my code and group like code. That brought the size of my code down to 50 lines.

Just so you know, it can be done. My program ran in .17 seconds to give a text answer of how the disks should be flipped and rotated. Just for some help, I didn’t mess around with any graphics until I sent the solution to @Bri_G showing the positions/rotation of the 4 disks for verification. I thought it was a very interesting challenge. Thanks @Bri_G. I’ll post my code at a later date just so I don’t spoil it for others.

@dave1707 - how have you measured the time at less than a second. Always thought Lua’s minimum time was a second or zero?

@Bri_G I use gettime from sockets. Keep changing the number of zeroes in the for loop and watch the time change by a factor of ten.

function setup()
    s=require("socket")
    st=s:gettime()
    for z=1,10000 do
        a=math.sqrt(z)
    end
    en=s:gettime()
    print(en-st)
end

Here’s my code that searches for solutions, ignoring dead ends. I didn’t compress it or add a lot of comments. It does the whole search in about .000082s (8.2015991210938e-05). There are 45 valid matches. In a copy of this code, I added print statements so I could see what was happening. Using cut outs of the disks, I followed what was going on at each of the searches and everything looked OK. I going to try something else manually to verify what’s happening here. I’ll post that when I finish, but I thought I’d post this anyways.

function setup()
    s=require("socket")
    st=s:gettime()        
    s1={{"lt","tl"},{"pf","fp"},{"up","pu"},{"lr","rl"},{"px","xp"},{"xl","lx"},
        {"xt","tx"},{"pu","up"},{"pr","rp"},{"rx","xr"},{"pf","fp"},{"ft","tf"},
        {"tp","pt"},{"rx","xr"},{"xl","lx"},{"rl","lr"},}    
    s2={{"lf","fl"},{"fp","pf"},{"rp","pr"},{"pt","tp"},{"ft","tf"},{"xp","px"},
        {"rl","lr"},{"fx","xf"},{"tr","rt"},{"px","xp"},{"fx","xf"},{"rf","fr"},
        {"xr","rx"},{"ft","tf"},{"tl","lt"},{"tx","xt"},}    
    s3={{"lr","rl"},{"pr","rp"},{"lf","fl"},{"rt","tr"},{"lt","tl"},{"pu","up"},
        {"fr","rf"},{"xu","ux"},{"xf","fx"},{"rp","pr"},{"xu","ux"},{"rf","fr"},
        {"lx","xl"},{"tr","rt"},{"tp","pt"},{"xt","tx"},}
    s4={{"lx","xl"},{"tf","ft"},{"tx","xt"},{"xf","fx"},{"tl","lt"},{"fl","lf"},
        {"ux","xu"},{"fr","rf"},{"xr","rx"},{"pt","tp"},{"rt","tr"},{"up","pu"},
        {"xp","px"},{"tf","ft"},{"fl","lf"},{"ux","xu"},} 
    searchCount=0
    for a=1,16 do   -- try each animal pair on disk 1
        d1(a)
    end
    print("Total time "..s:gettime()-st) 
    print("Search count ",searchCount)
    print("***  Slide window top up ***")
end

function d1(pos)
    f11=pos         -- disk 1 position in table
    c11=s1[f11][1]  -- get image pair from table
    m11=s1[f11][2]  -- get mirror of c11 from table
    f13=f11+2       -- disk 1 position 3 (3 oclock)
    if f11<9 then   -- keep value in range 1-8 or 9-16 based on pos value
        if f13>8 then
            f13=f13-8
        end
    elseif f13>16 then
        f13=f13-8
    end    
    c13=s1[f13][1]  -- image pair
    m13=s1[f13][2]  -- mirror of c13
    s27=1   -- initial search position for disk 2 position 7
    f27=0   -- initial find position for disk 2 position 7
    while f27~=nil do
        m1327(m13)  -- disk 1 position 3, disk 2 position 7
        if f27~=nil then
            s35=1   -- initial search position for disk 3 position 5
            f35=0   -- initial find position for disk 3 position 5
            while f35~=nil do
                m1135(m11)  -- disk 1 position 1, disk 3 position 5
                if f35~=nil then
                    s47=1
                    f47=0
                    while f47~=nil do
                        m3347(m33)  -- disk 3 position 3, disk 4 position 7
                        if f47~=nil then
                            m2145() -- disk 2 position 1, disk 4 position 5 
                            if c11==m35 and c13==m27 and c33==m47 and c21==m45 then
                                print("=====  Match found  =====")
                                print("(1-8)=Front  (9-16)=Back")
                                print("D1="..f11.."  D2="..f27.."  D3="..f35.."  D4="..f47)
                                print("Animal pairs at 6 o'clock position")
                                print("disk 1 ",s1[pos][1])
                                print("disk 2 ",s2[f27+2][1])
                                print("disk 3 ",s3[f35-4][1])
                                print("disk 4 ",s4[f47-6][1])
                            end
                        end
                    end
                end
            end
        end
    end
end

function srch(tab,ch,st)    -- search table tab for animal pair ch, start at st. 
    searchCount=searchCount+1
    for z=st,16 do
        if tab[z][1]==ch then
            return z
        end
    end
    return nil    
end

function m1327(a)
    if s27>16 then
        f27=nil
    else
        f27=srch(s2,a,s27)
        if f27~=nil then
            s27=f27+1
            c27=s2[f27][1]  -- image pair
            m27=s2[f27][2]  -- mirror of c27
            f21=f27-6
            if f27>8 then
                if f21<9 then
                    f21=f21+8
                end
            elseif f21<1 then
                f21=f21+8
            end
            c21=s2[f21][1]  -- image pair
            m21=s2[f21][2]  -- mirror of c21
        end
    end
end

function m1135(a)
    if s35>16 then
        f35=nil
    else
        f35=srch(s3,a,s35)
        if f35~=nil then
            s35=f35+1
            c35=s3[f35][1]  -- image pair
            m35=s3[f35][2]  -- mirror of c35
            f33=f35-2
            if f35>8 then
                if f33<9 then
                    f33=f33+8
                end
            elseif f33<1 then
                f33=f33+8
            end
            c33=s3[f33][1]  -- image pair
            m33=s3[f33][2]  -- mirror of c33
        end
    end
end

function m3347(a)
    if s47>16 then
        f47=nil
    else
        f47=srch(s4,a,s47)
        if f47~=nil then
            s47=f47+1
            c47=s4[f47][1]  -- image pair
            m47=s4[f47][2]  -- mirror of c47
        end
    end
end

function m2145()
    f45=f47-2
    if f47>8 then
        if f45<9 then
            f45=f45+8
        end
    elseif f45<1 then
        f45=f45+8
    end
    c45=s4[f45][1]  -- image pair
    m45=s4[f45][2]  -- mirror of c45
end

I’ve added a link to my code to my explanation post above. For completeness, here it is again.

@LoopSpace I like the way you display the disks. Maybe if I keep looking at your code, it’ll start to make sense. But right now it doesn’t. I’ll have to reread your explanation also.

@LoopSpace - very impressive and well annotated. Some aspects of your code are completely new to me. Never seen co-routines used. A lot of learning here for me. Thanks.

@Bri_G coroutines are amazing. I feel as though I’ve not even begun to explore their full potential, but particularly when trying to do something across several draw loops, they can be really versatile.

If I were just interested in getting the solution, then I wouldn’t use them here - @dave1707’s approach is a bit more direct (which is probably why it is a smidgeon faster). I use coroutines so that I can monitor the search.

@dave1707 I’ve had a look at your code. In essence, it’s doing something very similar to mine. The main difference, I think, is that you have the discs in a fixed position (am I right in thinking that you only test one ordering of the discs? That would make sense of the 45 configurations you’re getting - if you did all 6 orderings you’d get roughly 6 x 45 = 270 configurations which is close to my count of 224) and test the different rotations. My version is a bit more like how you would do a jigsaw: you have an edge to match and try all pieces that match that edge.

@LoopSpace You’re correct, I only did the one orientation, 1,2 across the top and 3,4 along the bottom. We determined long ago that there was only one solution, so there wasn’t any point on trying the others. I’ll have to try the other combinations and see if I get 45 searches in the others or if I get different values. Maybe they’ll add up to 224.

All - just updated the website again, minor update I just added a couple of pictures of the veteran computers and a few notes about them.

@LoopSpace I tried the other 5 configurations and the total was 276.

PS. @LoopSpace I’m having a hard time following your code, but does your code account for duplicate animal pairs on some of the disks. For instance, if you find a match on two disks but that doesn’t lead to a match on the third disk, do you continue the search on the second disk for the duplicate pair. For instance, disk 2 has 2 frog,tiger and 2 frog,fox.

@LoopSpace I tried the 24 different combination and came up with different total counts, which I kind of suspected. The upper left disk always went thru the 16 different animal pairs, but the other 3 disks only did searches based on the valid matches. So different starting pairs resulted in different search paths with different counts.

1234, 1243, 1324, 1342, 1423, 1432 had total 276
2134, 2143, 2314, 2341, 2413, 2431 had total 301
3124, 3142, 3214, 3241, 3412, 3421 had total 321
4123, 4132, 4213, 4231, 4312, 4321 had total 330

The 4 solutions were the same and the orientations were
(12)(24)(43)(31)
(34)(13)(21)(42)

@LoopSpace - I’ve just been looking through your code and pulling out bits and pieces to try. Octagon building really useful, have one from an old polygon post on the forum but am trying to find a mesh routine to build up the mats from first principles in Codea (just a distraction for me - ignore) but - whilst doing it I looked through your list of emojis in the symbols table and couldn’t understand the layout and how all edges could be built from it. Switched robot for bear and pumpkin for koala. Could you explain how it works?

@LoopSpace - forget it figured it out.

All,
For those who haven’t visited the website I am posting my code for resolution of the puzzle. It is not the most concise, nor the best programming - merely my attempt at the solution. I am continuing to work on thhis in a number of ways - looking for new solutions, presentation of output and others, which I will post later. If you need any information on how I arrived at this I can post a description later. First the code listing:


function setup()
    s = require("socket")
    pA,pB,pC,pD,lev1,lev2,lev3,lev4 = 0,0,0,0,0,0,0,0
    mirrors = {"00","00","00","00","00","00","00","00"}
    find = false
    font("Baskerville-Bold")
    fontSize(32)
    count, hits = 0,0
    solutions = {}
    solTimer = {}
    st = s:gettime()
    engine()
end

function draw()
    background(40, 40, 50)
    text(count,384,280)
    if find == true then
        fill(250, 255, 0, 255)
        text("WE HAVE "..hits.. " SOLUTIONS !!! ",384,232)
        for sol = 1,#solutions do
            text(solutions[sol],384,200)
            text(solTimer[sol].." secs",384,150)
        end
    else
        fill(244, 33, 59, 255)
        text("SORRY NO SOLUTION!!! ",364,200)
    end
end

function engine()
    limit = 16
    for sh = 1,6 do
        shuffle(sh)
        for sA = 1,limit do
            if sA <= 8 then dA = 1 fA ="F" else dA = 2 fA = "B" sA = sA-8 end
            dAS = (pA-1)*2+dA
            mirStr = double[dAS]
            mirrors[1] = string.sub(mirStr,(sA-1)*2+5,(sA-1)*2+6)
            mirrors[2] = string.sub(mirStr,(sA-1)*2+9,(sA-1)*2+10)
            for sB = 1,limit do
                if sB <= 8 then dB = 1 fB ="F" else dB = 2 fB = "B" sB = sB-8 end
                dBS = (pB-1)*2+dB
                mirStr = double[dBS]
                mirrors[3] = string.sub(mirStr,(sB-1)*2+9,(sB-1)*2+10)
                mirrors[4] = string.sub(mirStr,(sB-1)*2+13,(sB-1)*2+14)
                for sC = 1,limit do
                    if sC <= 8 then dC = 1 fC ="F" else dC = 2 fC = "B" sC = sC-8 end
                    dCS = (pC-1)*2+dC
                    mirStr = double[dCS]
                    mirrors[5] = string.sub(mirStr,(sC-1)*2+13,(sC-1)*2+14)
                    mirrors[6] = string.sub(mirStr,(sC-1)*2+1,(sC-1)*2+2)
                    for sD = 1,limit do
                        if sD <= 8 then dD = 1 fD ="F" else dD = 2 fD = "B" sD = sD-8 end
                        dDS = (pD-1)*2+dD
                        mirStr = double[dDS]
                        mirrors[7] = string.sub(mirStr,(sD-1)*2+1,(sD-1)*2+2)
                        mirrors[8] = string.sub(mirStr,(sD-1)*2+5,(sD-1)*2+6)
                        checkIt()
                        count = count+1
                    end
                end
            end
        end
    end
    en = s:gettime()
    print("Completion : "..en-st)
end

function shuffle(n)
    pos = (n-1)*4+1
    pA = routePlan[pos]
    pB = routePlan[pos+1]
    pC = routePlan[pos+2]
    pD = routePlan[pos+3]
    orientation = "Orientation: "..pA..pB..pC..pD
 --   print(orientation)
end

function switch(str)
    local temp = string.reverse(str)
    return temp
end

function checkIt()
    level = "Lev"
    if mirrors[1] == switch(mirrors[4]) then lev1 = lev1+1 end
    if mirrors[3] == switch(mirrors[6]) then lev2 = lev2+1 end
    if mirrors[5] == switch(mirrors[8]) then lev3 = lev3+1 end
    if mirrors[7] == switch(mirrors[2]) then lev4 = lev4+1 end
    level = level.."-"..lev1.."-"..lev2.."-"..lev3.."-"..lev4
    if mirrors[1] == switch(mirrors[4]) and mirrors[3] == switch(mirrors[6]) and mirrors[5] == switch(mirrors[8]) and mirrors[7] == switch(mirrors[2]) then
    find = true
    hits = hits+1
    solution = mirrors[1].."("..pA..fA..")-"..mirrors[3].."("..pB..fB..")-"..mirrors[5].."("..pC..fC..")-"..mirrors[7].."("..pD..fD..")"
    now = s:gettime()
    table.insert(solutions,hits,solution)
    table.insert(solTimer,hits,now-st)
    end
end

Second the data used in the solution.


double = {
    "PTTFFPRBBPLBLRRBPTTFFPRBBPLBLRRB",
    "RPBLPHFPTLHPTRLRRPBLPHFPTLHPTRLR",
    "BRFBRFRPBTRTLTTFBRFBRFRPBTRTLTTF",
    "TFTPPBPFFLRFLBPRTFTPPBPFFLRFLBPR",
    "RLFBHRPBFRTRPTBTRLFBHRPBFRTRPTBT",
    "TLTBFLBPBLHRBFHPTLTBFLBPBLHRBFHP",
    "PRPHTBTPBRRHLFFTPRPHTBTPBRRHLFFT",
    "LTFRRTFTRLBFRHLFLTFRRTFTRLBFRHLF"
    }

routePlan = {1,2,3,4,1,2,4,3,1,3,2,4,1,3,4,2,1,4,2,3,1,4,3,2}

disc = {"BeerMatPuz:BM3001F",
    "BeerMatPuz:BM3001B",
    "BeerMatPuz:BM3002F",
    "BeerMatPuz:BM3002B",
    "BeerMatPuz:BM3003F",
    "BeerMatPuz:BM3003B",
    "BeerMatPuz:BM3004F",
    "BeerMatPuz:BM3004B"
}

Note - thanks to all who have contributed - in particular @LoopSpace and @dave1707 who posted two excellent solutions.

p.s. for those still working on this, just ignore this post.