## The Hex Box

### The Hex Box

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|789B 234|567|891C 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|234B 234|567|891C 345|678|912  ---+---+---     |789|     |891|     |912|`

Move F3 would then look like this:
Code: Select all
`      DEF     |129|     |231|     |342|  ---+---+---A 567|893|234B 234|564|891C 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|277B 917|546|888C 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.

stranac

Posts: 1790
Joined: Thu Feb 07, 2013 3:42 pm

### Re: The Hex Box

Stranac,

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

https://projecteuler.net/problems
Pandora-Box

Posts: 10
Joined: Sat Apr 05, 2014 8:44 am

### Re: The Hex Box

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
• Use code tags when posting code.
• Include any errors with your post (in code tags).
• Make examples the minimum length to demonstrate your issue.

Mekire

Posts: 1711
Joined: Thu Feb 07, 2013 11:33 pm
Location: Tucson, Arizona

### Re: The Hex Box

I didn't very much
Pandora-Box

Posts: 10
Joined: Sat Apr 05, 2014 8:44 am

### Re: The Hex Box

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?

CodeAbbey - programming problems for novice coders (+ certificates)

RodionGork

Posts: 36
Joined: Sun Nov 16, 2014 7:04 am
Location: Saint-Petersburg, Russia

### Re: The Hex Box

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.

stranac

Posts: 1790
Joined: Thu Feb 07, 2013 3:42 pm

### Re: The Hex Box

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 sysz = sys.versionif z[0] == '2':    import Tkinter as tk    import tkMessageBox as tmelse:    import tkinter as tk    import tkinter.messagebox as tmimport hashlibimport copyclass 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
Screenshot.gif (14.59 KiB) Viewed 7405 times

Larz60+
Larz60+

Posts: 1307
Joined: Thu Apr 03, 2014 4:06 pm