The Hex Box

The Hex Box

Postby stranac » Thu Apr 17, 2014 6:45 pm

So a few years back (I think about the time I was starting with python), I stumbled upon this website with programming challenges: http://www.osix.net/
The first 8 problems (in the Geek Challenge category), I solved pretty easily, but I got stuck on number 9, and eventually gave up.

I have some free time over the next week or so, so I thought I'd give it another go.
Honestly, I still don't know exactly how to approach it, but hopefully I'll come up with something.

So, if anyone wants to join me, here's the challenge:
Level 9 : The Hex Box wrote:
Code: Select all
      DEF

     |123|
     |234|
     |345|
  ---+---+---
A 123|456|789
B 234|567|891
C 345|678|912
  ---+---+---
     |789|
     |891|
     |912|

Here is something I call a Hex Box. It is a device where you can move any row
or column as long as there are nine numbers in that row or column. You can
therefore only move three rows and three columns (rows ABC and columns DEF).

You are only allowed to shift a row right and a column down. When a row or
column is shifted, the last number gets placed back in the first.

The reason why this is called a Hex Box is for the way each shift is denoted.
Any move is denoted by it's row or column, then the number of shifts.
For example, a move "A5" means row A gets shifted to the right 5 times.
A move of A5 would look like this (assuming you started with the above.)
Code: Select all
      DEF

     |123|
     |234|
     |345|
  ---+---+---
A 567|891|234
B 234|567|891
C 345|678|912
  ---+---+---
     |789|
     |891|
     |912|

Move F3 would then look like this:
Code: Select all
      DEF

     |129|
     |231|
     |342|
  ---+---+---
A 567|893|234
B 234|564|891
C 345|675|912
  ---+---+---
     |781|
     |897|
     |918|

So the move order at this point would be A5F3.

With that information try to figure out the move order for this Hex Box,
starting from the setup at the top of the page:
Code: Select all
      DEF

     |212|
     |423|
     |534|
  ---+---+---
A 649|125|277
B 917|546|888
C 123|863|999
  ---+---+---
     |983|
     |134|
     |751|

The solution will be the 24-hex digit move order and as there are multiple
answers use the solution that has an MD5 checksum of
113DAC12B9A54D39944668499F72083D.

Hint: Every row and column gets moved twice (not the number of shifts) and a
row or column isn't moved again until the other five have been moved first
(ie. F4B2A3C7D5E8E2A5C7F1D5B7).


P.S. I would recommend you try the other challenges as well, IIRC they were pretty good. But I might be wrong, it's been a while...
Friendship is magic!

R.I.P. Tracy M. You will be missed.
User avatar
stranac
 
Posts: 1486
Joined: Thu Feb 07, 2013 3:42 pm

Re: The Hex Box

Postby Pandora-Box » Tue Apr 22, 2014 1:02 pm

Stranac,

Also there is another site which I know of and has quiet some practice problems

https://projecteuler.net/problems
Pandora-Box
 
Posts: 5
Joined: Sat Apr 05, 2014 8:44 am

Re: The Hex Box

Postby Mekire » Tue Apr 22, 2014 1:17 pm

Pandora-Box wrote:Also there is another site which I know of and has quiet some practice problems

https://projecteuler.net/problems

You clearly didn't notice that our board has a subforum (albeit not particularly active) dedicated to Project Euler:
http://python-forum.org/viewforum.php?f=27

-Mek
New Users, Read This
  • Use code tags when posting code.
  • Include any errors with your post (in code tags).
  • Describe your problem; not your chosen solution.
  • Make examples the minimum length to demonstrate your issue.
User avatar
Mekire
 
Posts: 1500
Joined: Thu Feb 07, 2013 11:33 pm
Location: Tucson, Arizona

Re: The Hex Box

Postby Pandora-Box » Tue Apr 22, 2014 1:22 pm

I didn't very much
Pandora-Box
 
Posts: 5
Joined: Sat Apr 05, 2014 8:44 am

Re: The Hex Box

Postby RodionGork » Thu Nov 20, 2014 9:45 am

Curious puzzle! Is there no way to see it at original site (osix?) without registering there?

Does it have a kind of interactive demo to play with it a bit - or it is up to users?
Image
CodeAbbey - programming problems for novice coders (+ certificates)
User avatar
RodionGork
 
Posts: 36
Joined: Sun Nov 16, 2014 7:04 am
Location: Saint-Petersburg, Russia

Re: The Hex Box

Postby stranac » Thu Nov 20, 2014 10:39 am

I think you have to register and solve the previous challenges to see it.

No interactive demo, but there is a hint available, which is a state of the box after each row and column is moved at least once.
This sounded like seeing it made the task pretty much trivial, so I haven't taken a look.

Haven't gotten around to messing with this at all since I made that post, thanks for reminding me about it.
Friendship is magic!

R.I.P. Tracy M. You will be missed.
User avatar
stranac
 
Posts: 1486
Joined: Thu Feb 07, 2013 3:42 pm

Re: The Hex Box

Postby Larz60+ » Tue May 12, 2015 8:48 pm

Hello,

I was playing around a bit with this puzzle the past few days, and didn't like having to make a move, and then calculate the MD5 each time, etc.
So I built a GUI that does all that.
If anyone wishes to modify this code, feel free to do so.
If you see any blatant errors, please give full details, and I'll correct the problem.
The GUI allows for an undo. If this is taboo, let me know and I'll comment it out.

Simple to use, If you wish to make the move A3, just click on A 3 times. The move history
and MD5 are updated each time.
MD5 is calculated on the Move History, not on the puzzle, hope this is correct
Enjoy
Code: Select all
#
# Hex.py - Hex puzzle GUI
#
# Author: Larry McCaig (Larz60+)
#
#    Hex.py is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as
#    published by the Free Software Foundation, either version 3 of
#    the License, or (at your option) any later version.
#
#    Hex.py is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#    GNU Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with viewTestResults.py. If not, see
#    <http://www.gnu.org/licenses/>.
#
#    If you use this module and find bugs, please leave me a note, and give
#    as much information leading up to the bug as possible.
#
import sys

z = sys.version
if z[0] == '2':
    import Tkinter as tk
    import tkMessageBox as tm
else:
    import tkinter as tk
    import tkinter.messagebox as tm
import hashlib
import copy

class Hex:
    def __init__(self, parent):
        self.w = parent
        self.w.title('Hex')
        self.bg1='#FFFABC'
        self.bg2='#E8B69B'

        # Cells containing 0 cannot be changed, and do not
        # contain cubes. Cells are named x0y0, x0y1, etc
        self.origcellmap = [
            ['0', '0', '0', '0', 'D', 'E', 'F', '0', '0', '0'],
            ['0', '0', '0', '0', '1', '2', '3', '0', '0', '0'],
            ['0', '0', '0', '0', '2', '3', '4', '0', '0', '0'],
            ['0', '0', '0', '0', '3', '4', '5', '0', '0', '0'],
            ['A', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
            ['B', '2', '3', '4', '5', '6', '7', '8', '9', '1'],
            ['C', '3', '4', '5', '6', '7', '8', '9', '1', '2'],
            ['0', '0', '0', '0', '7', '8', '9', '0', '0', '0'],
            ['0', '0', '0', '0', '8', '9', '1', '0', '0', '0'],
            ['0', '0', '0', '0', '9', '1', '2', '0', '0', '0']
        ]

        self.cellmap  = copy.deepcopy(self.origcellmap)    # want a copy, not a reference
        self.movedisp = None
        self.md5disp  = None
        self.md5val   = None
        self.Md5tv    = tk.StringVar()

        # self.buttons = {}
        self.f1 = tk.Frame(self.w, bd=3, bg=self.bg1, padx=10, pady=10, relief=tk.RAISED)
        self.f1.pack(fill=tk.BOTH, expand=1, anchor='center')
        self.cells = {}

        self.moveHistory = []
        self.moveHistorystr = ''
        self.MvHistory = tk.StringVar()
        self.NumberOfMoves = 0
        self.l2 = None

        self.debug=False

    def resetPuzzle(self):
        self.cellmap = []
        self.cellmap = copy.deepcopy(self.origcellmap)
        self.redisplayCellmap()
        self.moveHistory = ''
        self.NumberOfMoves = 0
        self.Md5tv.set('')

    def calcMD5(self):
        self.md5val = hashlib.md5()
        self.md5val.update(self.moveHistorystr.encode('utf-8'))
        self.Md5tv.set(self.md5val.hexdigest())

    def addCells(self):
        cm = self.cellmap
        cells = self.cells
        f1 = self.f1
        bg1 = self.bg1
        bg2 = self.bg2
        cx = None

        for x in range(len(cm)):
            for y in range(len(cm[x])):
                cx = x
                name = 'x{}y{}'.format(x,y)
                value = cm[x][y]

                if value >= 'A' and value <= 'F':
                    xrelief=tk.RAISED
                else:
                    xrelief=tk.SUNKEN

                cells[name]=(tk.Label(f1, text=value,
                    bd=1, height=2, width=4, bg=bg2, relief=xrelief), [value])
                cells[name][0].grid(row=x, column=y, sticky='nsew')

                if value == '0':
                    cells[name][0].configure(bg=bg1)
                    cells[name][0].configure(bd=0)
                    cells[name][0].configure(relief=tk.FLAT)
                    cells[name][0].configure(text='')

                def eventRedirect(event, self=self, value=value):
                    return self.shuffle(event, value)

                if value >= 'A' and value <= 'F':
                    cells[name][0].bind('<Button-1>', eventRedirect)
        cx += 1
        b1 = tk.Button(f1, relief=tk.RAISED, bd=2, text='Undo', command=self.undo)
        b1.grid(row=cx, column=0, columnspan=2, padx=2, pady=2, sticky='ew')

        b2 = tk.Button(f1, relief=tk.RAISED, bd=2, text='Reset', command=self.resetPuzzle)
        b2.grid(row=cx, column=2, columnspan=2, padx=2, pady=2, sticky='ew')

        l1= tk.Label(f1, text='Number of Moves', padx=2, pady=2, bd=0, bg=bg1, relief=tk.FLAT)
        l1.grid(row=cx, column=4, columnspan=2)

        self.l2 = tk.Label(f1, text='', bd=2, padx=2, pady=2, bg='White', relief=tk.SUNKEN)
        self.l2.grid(row=cx, column=6, columnspan=2, sticky='ew')

        cx += 1
        l3 = tk.Label(f1, text='Move History', bd=0, bg=bg1, relief=tk.FLAT)
        l3.grid(row=cx, column=0, columnspan=2)

        xscrollbar1=tk.Scrollbar(f1, orient=tk.HORIZONTAL)
        xscrollbar1.grid(row=cx+1, column=2, columnspan=8, sticky=tk.EW)

        self.movedisp = tk.Entry(f1, textvariable=self.MvHistory, bd=1, bg='White',
            relief=tk.SUNKEN, xscrollcommand=xscrollbar1.set);

        self.movedisp.grid(row=cx, column=2, columnspan=8, padx=2, pady=2, sticky ='ew')
        xscrollbar1.config(command=self.movedisp.xview)

        cx += 2
        l4 = tk.Label(f1, text='MD5', bd=0, bg=bg1, relief=tk.FLAT)
        l4.grid(row=cx, column=0)

        xscrollbar2=tk.Scrollbar(f1, orient=tk.HORIZONTAL)
        xscrollbar2.grid(row=cx+1, column=1, columnspan=10, sticky=tk.EW)

        self.md5disp = tk.Entry(f1, textvariable=self.Md5tv, bd=1, bg='White',
            relief=tk.SUNKEN, xscrollcommand=xscrollbar2.set);
        self.md5disp.grid(row=cx, column=1, columnspan=10, padx=2, pady=2, sticky ='ew')
        xscrollbar2.config(command=self.md5disp.xview)
        cx += 2

    def updateMoveHistoryDisplay(self, undo=False):
        self.moveHistorystr = ''
        for letter in self.moveHistory:
            self.moveHistorystr = self.moveHistorystr + '{}'.format(letter)
        self.MvHistory.set(self.moveHistorystr)
        self.calcMD5()
        if undo:
            self.NumberOfMoves -= 1
        else:
            self.NumberOfMoves += 1
        self.l2.configure(text = str(self.NumberOfMoves))
        self.w.update_idletasks()

    def updateHistory(self, value):
        lhist = len(self.moveHistory)
        if lhist:
            prevval = self.moveHistory[lhist-2]
            if prevval == value:
                 self.moveHistory[lhist-1] += 1
            else:
                self.moveHistory.append(value)
                self.moveHistory.append(1)
        else:
            self.moveHistory.append(value)
            self.moveHistory.append(1)
        self.updateMoveHistoryDisplay()

    def redisplayCellmap(self):
        for x in range(len(self.cellmap)):
            for y in range(len(self.cellmap[x])):
                if self.cellmap[x][y] == '0':
                    continue
                name = 'x{}y{}'.format(x,y)
                self.cells[name][0].configure(text=self.cellmap[x][y])

    def incrementCell(self, x, y, undo=False):
        cm = self.cellmap
        c0 = ord('0')
        oldcell = cm[x][y]
        if undo:
            nc = ord(oldcell) - c0 + 1
            if nc == 10: nc = 1
        else:
            nc = ord(oldcell) - c0 - 1
            if nc == 0: nc = 9
        newcell = chr(nc + c0)
        cm[x][y] = newcell

    def undo(self):
        id = None
        if len(self.moveHistory) == 0:
            tm.showerror('Hex', 'Nothing to Undo')
        else:
            lhist = len(self.moveHistory)
            if lhist:
                id = self.moveHistory[lhist-2]
                cellv = ord(id) - ord('A') + 1
                if id < 'D':
                    cellv += 3
                    x = cellv
                    for y in range(10):
                        if y == 0: continue
                        self.incrementCell(x,y, undo=True)
                else:
                    for x in range(10):
                        y = cellv
                        if x == 0: continue
                        self.incrementCell(x,y, undo=True)
                if self.moveHistory[lhist-1] > 1:
                    self.moveHistory[lhist-1] -= 1
                else:
                    del self.moveHistory[lhist-1]
                    del self.moveHistory[lhist-2]
            self.updateMoveHistoryDisplay()
            self.redisplayCellmap()

    def shuffle(self, event, value):
        cellv = ord(value) - ord('A') + 1
        if value < 'D':
            cellv += 3
            x = cellv
            for y in range(10):
                if y == 0: continue
                self.incrementCell(x,y)
        else:
            y = cellv
            for x in range(10):
                if x == 0: continue
                self.incrementCell(x, y)
        self.updateHistory(value)
        self.redisplayCellmap()

    def puzzle(self):
        self.addCells()

if __name__ == '__main__':
    root = tk.Tk()
    h = Hex(root)
    h.puzzle()
    root.mainloop()


Screenshot.gif
Screenshot
Screenshot.gif (14.59 KiB) Viewed 1011 times


Larz60+
Larz60+
 
Posts: 673
Joined: Thu Apr 03, 2014 4:06 pm


Return to Challenges

Who is online

Users browsing this forum: Yahoo [Bot] and 0 guests