You are on page 1of 59

The Magnetic Tower of Hanoi

Uri Levy
Atlantium Technologies, Har-Tuv Industrial Park, Israel

Abstract
In this work I study a modified Tower of Hanoi puzzle, which I term
Magnetic Tower of Hanoi (MToH). The original Tower of Hanoi puzzle,
invented by the French mathematician Edouard Lucas in 1883, spans "base
2". That is – the number of moves of disk number k is 2^(k-1), and the total
number of moves required to solve the puzzle with N disks is 2^N - 1. In
the MToH puzzle, each disk has two distinct-color sides, and disks must be
flipped and placed so that no sides of the same color meet. I show here that
the MToH puzzle spans "base 3" - the number of moves required to solve
an N+1 disk puzzle is essentially three times larger than he number of
moves required to solve an N disk puzzle. The MToH comes in 3 flavors
which differ in the rules for placing a disk on a free post and therefore
differ in the possible evolutions of the Tower states towards a puzzle
solution. I analyze here algorithms for minimizing the number of steps
required to solve the MToH puzzle in its different versions. Thus, while the
colorful Magnetic Tower of Hanoi puzzle is rather challenging, its inherent
freedom nurtures mathematics with remarkable elegance.

-1-
The Classical Tower of Hanoi
The classical Tower of Hanoi (ToH) puzzle[1,2,3] consists of three posts, and
N disks. The puzzle solution process ("game") calls for one-by-one disk
moves restricted by one "size rule". The puzzle is solved when all disks are
transferred from a "Source" Post to a "Destination" Post.

Figure 1: The classical Tower of Hanoi puzzle. The puzzle consists of three
posts, and N disks. The puzzle solution process ("game") calls for
one-by-one disk moves restricted by one "size rule". The puzzle is
solved when all disks are transferred from a "Source" Post to a
"Destination" Post. The minimum number of disk-moves
necessary to solve the ToH puzzle with N disks is 2N – 1.

Let's define the ToH puzzle in a more rigorous way.

1.1. The Classical Tower of Hanoi – puzzle description


A more rigorous description of the ToH puzzle is as follows -
Puzzle Components:
• Three equal posts
• A set of N different-diameter disks
Puzzle-start setting:
• N disks arranged in a bottom-to-top descending-size order on a
"Source" Post (Figure 1)
Move:
• Lift a disk off one Post and land it on another Post

-2-
Disk-placement rules:
• The Size Rule: A small disk can not "carry" a larger one (Never land
a large disk on a smaller one)
Puzzle-end state:
• N disks arranged in a bottom-to-top descending-size order on a
"Destination" Post (one of the two originally-free posts)

Given the above description of the classical ToH puzzle, let's calculate the
(minimum) number of moves necessary to solve the puzzle.

1.2. Number of moves


Studying the classical ToH puzzle in terms of required moves to solve the
puzzle, it is not too difficult to show[2,3] (prove) that the k-th disk will make
P(k ) moves given by

P(k ) = 2k−1 . (1)

Disk numbering is done of course from bottom to top and as Equation 1


states - the smallest disk ( k = N ), like the least significant bit in a "binary
speedometer", makes the largest number of moves.
The total number of moves S (N ) is given by the sum -

S (N ) =  2k−1 = 2N − 1 . (2)
k =1

Table 1 lists the (minimum) number of moves of each disk (Equation 1)


and the total (minimum) number of moves required to solve the classical
ToH puzzle (Equation 2) for the first eight stack heights.

-3-
k
1 2 3 4 5 6 7 8 SUM 2N - 1
N
1 1 1 1
2 1 2 3 3
3 1 2 4 7 7
4 1 2 4 8 15 15
5 1 2 4 8 16 31 31
6 1 2 4 8 16 32 63 63
7 1 2 4 8 16 32 64 127 127
8 1 2 4 8 16 32 64 128 255 255

Table 1: Minimum number of disk-moves required to solve the classical


Tower of Hanoi puzzle. N is the total number of disks participating
in the game and k is the disk number in the ordered stack,
counting from bottom to top. The k-th disk "makes" 2(k-1) moves
(Equation 1). The total number of disk-moves required to solve an
N-disk puzzle is 2N – 1 (Equation 2).

Table 1 clealy shows how (elegantly) the classical ToH spans base 2.
Let's see now how base 3 is spanned by the far more intricate Magnetic
Tower of Hanoi puzzle.

2. The Magnetic Tower of Hanoi


In the Magnetic Tower of Hanoi puzzle[4], we still use three posts and N
disks. However, the disk itself, the move definition and the game rules are
all modified (extended).
The rigorous description of the MToH puzzle is as follows:

Puzzle Components:
• Three equal posts
• A set of N different-diameter disks
• Each disk's "bottom" surface is colored Blue and its "top" surface is
colored Red

Puzzle-start setting:
• N disks arranged in a bottom-to-top descending-size order on a
"Source" Post (Figure 2)

-4-
• The Red surface of every disk in the stack is facing upwards (Figure
2). Note that the puzzle-start setting satisfies the "Magnet Rule" (see
below). And needless to say, Red is chosen arbitrarily without
limiting the generality of the discussion.

Move:
• Lift a disk off one post
• Turn the disk upside down and land it on another post

Disk-placement rules:
• The Size Rule: A small disk can not "carry" a larger one (Never land
a large disk on a smaller one)
• The Magnet Rule: Rejection occurs between two equal colors
(Never land a disk such that its bottom surface will touch a co-
colored top surface of the "resident" disk)

Puzzle-end state:
• N disks arranged in a bottom-to-top descending-size order on a
"Destination" Post (one of the two originally-free posts)

-5-
Figure 2: The Magnetic Tower of Hanoi puzzle. Top – puzzle-start setting.
The puzzle consists of three posts, and N two-color disks. The
puzzle solution process ("game") calls for one-by-one disk moves
restricted by two rules – the Size Rule and the Magnet Rule. The
puzzle is solved when all disks are transferred from a "Source"
Post to a "Destination" Post - bottom.

Given the above description of the MToH puzzle, let's calculate the
number of moves necessary to solve the puzzle.
We start by explicitly solving the N=1, N=2 and N=3 cases.

-6-
2.1. Explicit solution for the first three stacks of the MToH puzzle
The N = 1 case is trivial – move the disk from the Source Post to a
Destination Post (Figure 3).

Figure 3: The start-setting (top) and the end-state (bottom) for the N=1
MToH puzzle. The number of moves required to solve the puzzle
is P(1) = 1.

Thus, for the N=1 case we have

P(1) = 1 ; S (1) = 1 . (3)

Let's see the N=2 case.

-7-
2

4
3

Figure 4: The start-setting (top), an intermediate setting (center) and the


end-state (bottom) for the N=2 MToH puzzle. The number of
moves to progress from the start-setting to the intermediate state
described by the center figure is 2. The number of moves to
progress from the center-described state to the end-state
described by the bottom figure is again 2. Thus, the (minimum)
number of moves required to solve the puzzle is S(2) = 4. Note
that two different solution routes, both of length 4, exist (1,2,1,1 –
shown, 1,1,2,1 – not shown).

-8-
Consulting Figure 4 we find for the N=2 case -

S (2) = 4 . (4)

The small disk made 3 (=31) moves and the large disk made 1 (=30) move.
Thus far then, for the N=1 and N=2 cases, base 3 is elegantly spanned as

P(k ) = 3k−1 and S (N ) = (3N − 1) / 2 ; N = 1,2. (5)

Exactly analogous to the base 2 span by the classical ToH (Equation 1 and
Equation 2).
But let's see now the N=3 case.
To conveniently talk about the N=3 case, let's (arbitrarily and without loss
of generality) define the posts (refer to Figure 5 below) as
• S – the Source Post
• D – the Destination Post
• I – the (remaining) Intermediate Post
Let's also memorize the disk numbering convention:
• 1 – the largest disk
• 2 – the mid-size disk
• 3 – the smallest disk

Step # of
Disks From To Comments
number moves
1 2,3 S (Red) I (Blue) 4 Equation 4
2 1 S (Red) D (Blue) 1 S now free
3 3 I (Blue) D (Blue) 2 Through S, S now free
Post I now free
4 2 I (Blue) S (Red) 1
Fig. 5 - middle
S and I are both Red
5 3 D (Blue) I (Red) 1
I switched Blue→Red
6 2 S (Red) D (Blue) 1
7 3 I (Red) D (Blue) 1 Puzzle solved
11 Total # of moves
Table 2: Explicit description of the moves to solve the N=3 MToH puzzle.
The total number of moves is 11, which does NOT exactly coincide

-9-
with the "base 3 rule" (Equation 5).

- 10 -
S I D

S I D

S I D

Figure 5: The start-setting (top), an intermediate setting (center) and the


end-state (bottom) for the N=3 MToH puzzle. The number of
moves to progress from the start-setting to the intermediate state
described by the center figure is 8 (read the text for details). The
number of moves to progress from the center-described state to
the end-state described by the bottom figure is 3. Thus, the
number of moves required to solve the puzzle is S(3) = 11. The
S(3) number (11) breaks the perfect base 3 rule (Equation 5). We
therefore need to probe into the puzzle further in order to
decipher the mystery of this newly observed irregularity (and
come up with a modified rule).

- 11 -
Listed in table 2 are the moves required to solve the N=3 MToH puzzle.
As shown in Table 2 and as demonstrated by Figure 5 –

S (3) = 11. (6)

The resulting total number of moves violates the "base 3 rule" (should have
been 13, refer to Equation 5). The states of the puzzle before step 1 (puzzle-
start state), after step 4 and after step 7 (puzzle-end state) are shown by
Figure 5 – top, center, bottom respectively.
In order to decipher the mystery (of the newly observed irregularity) and to
progress with the analysis of the MToH puzzle, let's define a new magnetic
tower, refer to it as the "Colored Magnetic Tower of Hanoi" and study its
properties.

2.2. The Colored Magnetic Tower of Hanoi – the "100" solution


Studying the N=3 MToH puzzle, I realized that what breaks the base 3 rule
is the possibility of the smallest disk to move to a free post (step 5 in Table
2). By "free" I mean a post that is not "magnetized" or not "color coded". A
Neutral Post that can accept any-color disk. To suppress this freedom, let's
permanently color-code each post, call the restricted tower the Colored
Magnetic Tower of Hanoi (CMToH) and see what happens.

2.2.1. Definition of "Colored"


Let's start with a definition of a Colored MToH:
An MToH is "Colored" if (without loss of generality) its posts are pre-
colored (or "permanently colored")
Either as
1. Red-Blue-Blue
Or as
2. Red-Red-Blue.

Let's designate this (permanently) Colored MToH as CMToH.


The two versions of the newly defined CMToH puzzle are shown by
Figure 6. The moves to solve the CMToH puzzle with N=2, for each of its
versions, are explicitly detailed by Table 3. Note that the only difference
between the versions is the "timing" of the move of the big disk (after one
move of the small disk in the first version and after two moves of the small
disk in the second version).

- 12 -
S ID I
D

S I D

Figure 6: The two versions of the (permanently) Colored Magnetic Tower


of Hanoi. As shown in the text, the two definitions are equivalent
in terms of number of moves. Given a Colored Magnetic Tower
of Hanoi, the number of moves of disk k are P(k) = 3(k-1) and the
total number of moves is S(N) = (3N – 1)/2. Thus, the freshly
defined Colored Magnetic Tower of Hanoi strictly spans base 3.

Step # of
Disks From To Comments
number moves
1. Red-Blue-Blue
1 2 S (Red) I (Blue) 1
2 1 S (Red) D (Blue) 1
3 2 I (Blue) D (Blue) 2 Through Red S
2. Red-Blue-Red
1 2 S (Red) I (Red) 2 Through Blue D
2 1 S (Red) D (Blue) 1
3 2 I (Red) D (Blue) 1
Table 3: Explicit description of the moves to solve the N=2 CMToH
puzzle. The total number of moves for both versions is 4. And the
N=3 case of the CMToH puzzle is solved by 13 moves.

- 13 -
2.2.2. Expressions for the number of moves
Simple observations reveal that, as is the case with the classical ToH, the
"forward" moves solving the CMToH puzzle are deterministic.
Furthermore, it is not too difficult to show by a recursive argument (see the
proof for the classical ToH[2,3]) that the number of disk moves P100(k) and
(therefore) the total number of moves P100(N) perfectly span base 3:

P100 (k) = 3k−1 . (7)

and
N 3N −1
k −1

S100 (N ) = 3 =
k =1 31 −1 . (8)

The subscript "100" in Equations 7 and 8, relates to a solution "duration" of


100%.
Table 4 lists the (minimum) number of moves of each disk (Equation 7)
and the total (minimum) number of moves (Equation 8) required to solve
the CMToH puzzle for the first eight stack heights.

k
1 2 3 4 5 6 7 8 SUM (3N - 1)/2
N
1 1 1 1
2 1 3 4 4
3 1 3 9 13 13
4 1 3 9 27 40 40
5 1 3 9 27 81 121 121
6 1 3 9 27 81 243 364 364
7 1 3 9 27 81 243 729 1093 1093
8 1 3 9 27 81 243 729 2187 3280 3280

Table 4: Minimum number of disk-moves required to solve the Colored


Magnetic Tower of Hanoi puzzle. N is the total number of disks
participating in the game and k is the disk number in the ordered
stack, counting from bottom to top. The k-th disk "makes" 3(k-1)
moves (Equation 7). The total number of disk-moves required to
solve an N-disk puzzle is (3N – 1)/2 (Equation 8).
- 14 -
Having solved the rather simple Colored Magnetic Tower puzzle, we can
move on to solving the more intricate "Free" or "Dynamically Colored"
Magnetic Tower puzzle. As discussed below, we will identify "Free" and
"Colored" states of the "Dynamically Colored" MToH leading to a far
"shorter" solution (relative to the "100" solution) of the MToH puzzle.

2.3. The "67%" solution of the MToH puzzle


The color of posts in the MToH puzzle is determined by the color of the
disks it holds. The color is therefore "dynamic". During the game, the color
of a given post can be RED, can be BLUE, and can be Neutral. For moves
analysis, we can distinguish between three distinct MToH states.

2.3.1. Distinct states of the Magnetic Tower of Hanoi


After playing with the MToH puzzle for a while, one may realize that
actually three distinct tower states exist
• "Free" - two posts are Neutral ("start" and "end" states)
• "Semi-Free" – one post Neutral, the other two are oppositely Colored
• "Colored" – two posts are co-colored

- 15 -
import time
import pygame as pg
import adt
import models
import textrect
from language import game_dict
import sys

pg.init()
pg.mixer.init()

#
========================================================
======================

GAME_WIDTH = 900
GAME_HEIGHT = 600
GAME_FONT = "assets/fonto.ttf"
GAME_FONT2 = "assets/DK Jambo.otf"

BG_MUSIC = 'assets/music.mp3'

GAME_CAPTION = 'Tower of Hanoi!'


BACK_BUTTON = "assets/back.png"

#
========================================================
======================
# initialisasi game
gameDisplay = pg.display.set_mode((GAME_WIDTH, GAME_HEIGHT))
gameClock = pg.time.Clock()
gameIcon = pg.image.load('assets/logo.png')
pg.display.set_caption(GAME_CAPTION)
pg.display.set_icon(gameIcon)

#
========================================================
======================
# definisi objek-objek pembangun game:
def pgText(text, pos, font_size=18, font_name=GAME_FONT):
- 16 -
"""Menulis teks rata tengah warna hitam pada layar."""

#font = pg.font.Font(font_name, font_size)


#font = pg.font.SysFont("Arial", font_size)
font = pg.font.Font(pg.font.get_default_font(), font_size)
textSurf = font.render(text, True, adt.color_use("black"))
textRect = textSurf.get_rect()
textRect.center = pos
gameDisplay.blit(textSurf, textRect)

def pgParagraf(text, rect_info, font_name=GAME_FONT, font_size=18):


"""Menulis paragraf rata kiri warna hitam
dengan warna background windows pada layar"""

font = pg.font.Font(pg.font.get_default_font(), font_size)


#font = pg.font.SysFont("Arial", 18)
rect = pg.Rect(rect_info)
rendered = textrect.render_textrect(text, font, rect,
adt.color_use("black"),
adt.color_use("background"), 0)
if rendered:
gameDisplay.blit(rendered, rect.topleft)

def pgButton(msg, pos, size, col_still, col_hover, action=None):


"""Membuat button berisi teks"""

mouse = pg.mouse.get_pos()
click = pg.mouse.get_pressed()

# ubah warna button menjadi color_hover ketika mouse hover di button,


# dan kembalikan jadi color_still ketika mouse tidak ada di button.
if (pos[0] + size[0] > mouse[0] > pos[0]) and \
(pos[1] + size[1] > mouse[1] > pos[1]):
pg.draw.rect(gameDisplay, col_hover, (pos, size))
if click[0] == 1 and action is not None:
action()
else:
pg.draw.rect(gameDisplay, col_still, (pos, size))

pgText(msg, (pos[0]+size[0]/2, pos[1]+size[1]/2), 20)

- 17 -
def pgSlider(pos, var=True):
"""Membuat slider on/off"""

mouse = pg.mouse.get_pos()
click = pg.mouse.get_pressed()
size = (80, 80)

global game_mouse_click

# cek posisi dan perilaku mouse


if (pos[0]+20 + size[0] > mouse[0] > pos[0]-20) and \
(pos[1] + size[1] > mouse[1] > pos[1]):
if click[0] == 1 and not game_mouse_click:
var = not var
game_mouse_click = True
elif click[0] == 0:
game_mouse_click = False

# ubah gambar slider menurut state baru


if var:
# slider dalam posisi on
pgImage(pos, size, "assets/tog-on.png", action=None)
else:
pgImage(pos, size, "assets/tog-off.png", action=None)

return var

def pgImage(pos, size, file, action=None):


"""Membuat button berisi image"""

mouse = pg.mouse.get_pos()
click = pg.mouse.get_pressed()

# cetak image ke layar


raw = pg.image.load(file).convert_alpha()
img = pg.transform.scale(raw, size)
rect = pg.Rect(pos, size)
gameDisplay.blit(img, rect)

# ini akan diproses jika image berfungsi menjadi button


if (pos[0]+size[0] > mouse[0] > pos[0]) and \
(pos[1]+size[1] > mouse[1] > pos[1]) and \
- 18 -
click[0] == 1 and action is not None:
action()

def pgMusic():
"""Memainkan musik"""

global game_data
if game_data['sound']:
# musik menyala
pg.mixer.music.unpause()
else:
pg.mixer.music.pause()

#
========================================================
======================
# definisi fungsi-fungsi di game:
def GameQuit():
"""Keluar dari game."""
pg.quit()
quit()

def GameHomePage():
"""Menu utama dari game"""

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgText(game_dict[lang]['game_title'], (GAME_WIDTH/2, 100), 90)

# buat pilihan menu utama


pgImage((GAME_WIDTH/2-100, GAME_HEIGHT/2-100), (200, 200),
"assets/play-h.png", GameHistory)
pgImage((GAME_WIDTH/6-40, 450), (80, 80), "assets/about.png",
GameAbout)
pgImage((GAME_WIDTH/6*2-40, 450), (80, 80), "assets/secret.png",
GameSecret1)
pgImage((GAME_WIDTH/6*3-40, 450), (80, 80), "assets/hof.png",
- 19 -
GameHOF)
pgImage((GAME_WIDTH/6*4-40, 450), (80, 80), "assets/setting.png",
GameSetting)
pgImage((GAME_WIDTH/6*5-40, 450), (80, 80), "assets/exit.png",
GameQuit)

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

def GamePlay():
"""literally game hanoi-nya."""
global username

# Create main menu object


menu = models.MainMenu(GAME_WIDTH, GAME_HEIGHT)

# Create game object


game = models.Game(GAME_WIDTH, GAME_HEIGHT)

# Discs' move variables


done = False
drag = False
drop = False
move = False
game_over = False
init_game = False
disc_index = None
last_pos = [0, 0]

# Moves counter
moves_counter = 0
play_time = 0

# -------- Main Game Loop -----------


while not done:

# --- Main event loop


for event in pg.event.get():

if event.type == pg.QUIT:
done = True

- 20 -
elif event.type == pg.MOUSEBUTTONDOWN:
drag = True
drop = False

if init_game:
if not game_over:
for i in range(0, game.n_discs):
if game.discs[i].is_clicked():
current_pos = game.discs[i].current_pos
pos_length = len(game.pos[current_pos].discs)
if game.discs[i] ==
game.pos[current_pos].discs[pos_length-1]:
disc_index = i
last_pos = [game.discs[i].rect.x, game.discs[i].rect.y]
move = True
else:

# ranking hasil user


game_data['hof'] = adt.algo_addSorted(game_data['hof'],
game.n_discs,
[username, play_time, moves_counter])
# masukkan ke database
adt.data_write(game_data)

if menu.btn_quit.is_clicked():
done = True
GameHomePage()

if menu.btn_play_again.is_clicked():
game.sprites_list.remove(game.discs)
game.pos[2].discs = []
moves_counter = 0
game.discs = []
game.draw_discs()
game_over = False

if menu.btn_return.is_clicked():
menu.sprites_list.remove([menu.btn_play_again,
menu.btn_return, menu.btn_quit])
menu.sprites_list.add([menu.btn_discs, menu.label])
game.sprites_list.remove(game.discs)
init_game = False
else:
start_ticks = pg.time.get_ticks() # Waktu semenjak pg.init()
- 21 -
dimulai

for i in range(0, len(menu.btn_discs)):


if menu.btn_discs[i].is_clicked():
game.set_n_discs(menu.btn_discs[i].value)
game.sprites_list.remove(game.discs)
game.discs = []
game.pos[2].discs = []
moves_counter = 0
game.draw_discs()
init_game = True
game_over = False
break

elif event.type == pg.MOUSEBUTTONUP:


drag = False
drop = True

gameDisplay.fill(adt.color_use("background"))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

if init_game:

pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)


pgText(game_dict[lang]['play_info'].format(str(moves_counter),
str(round(play_time, 1))), (GAME_WIDTH/2, 120), 18)

if game_over:
start_ticks = pg.time.get_ticks() # Waktu semenjak pg.init() dimulai
part 2, bisa lebih efisien enggak ya?
menu.sprites_list.draw(gameDisplay)

if len(game.pos[2].discs) == game.n_discs:
if moves_counter == game.min_moves:
pgText(game_dict[lang]['congrats_min'], (GAME_WIDTH/2,
GAME_HEIGHT/3), 18)
else:
pgText(game_dict[lang]['congrats_aja'], (GAME_WIDTH/2,
GAME_HEIGHT/3), 18)

else:
play_time = (pg.time.get_ticks() - start_ticks)/1000 # Actual timer

if drag:
- 22 -
if move:
pos = pg.mouse.get_pos()
game.discs[disc_index].rect.x = pos[0] -
(game.discs[disc_index].width/2)
game.discs[disc_index].rect.y = pos[1] -
(game.discs[disc_index].height/2)

elif drop:
if move:
current_pos = game.discs[disc_index].current_pos
new_pos = None
change = False
turn_back = True
position = pg.sprite.spritecollideany(game.discs[disc_index],
game.pos_sprites_list)

if position is not None:


new_pos = position.pos_index
if new_pos != current_pos:
disc_length = len(position.discs)
if disc_length == 0:
turn_back = False
change = True
elif game.discs[disc_index].id > position.discs[disc_length-
1].id:
turn_back = False
change = True

if change:
moves_counter = moves_counter + 1
game.pos[current_pos].discs.remove(game.discs[disc_index])
game.discs[disc_index].current_pos = new_pos
game.pos[new_pos].discs.append(game.discs[disc_index])
new_pos_length = len(game.pos[new_pos].discs)
game.discs[disc_index].rect.x = game.pos[new_pos].rect.x - \

((game.DISC_WIDTH/(game.discs[disc_index].id+1)/2)-
(game.DISC_HEIGHT/2))
game.discs[disc_index].rect.y = (game.BOARD_Y -
game.DISC_HEIGHT) - (game.DISC_HEIGHT*(new_pos_length-1))

# Check if the game is over


if len(game.pos[2].discs) == game.n_discs:
game_over = True
- 23 -
menu.sprites_list.add([menu.btn_play_again,
menu.btn_quit, menu.btn_return])
menu.sprites_list.remove([menu.label, menu.btn_discs])

# ngatur kalau dilepas diudara


if turn_back:
game.discs[disc_index].rect.x = last_pos[0]
game.discs[disc_index].rect.y = last_pos[1]

move = False
game.sprites_list.draw(gameDisplay)

else:
menu.sprites_list.draw(gameDisplay)

# update gameDisplay.
pg.display.flip()
gameClock.tick(60)

pg.quit()

def GameAbout():
"""Menu utama dari game"""

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

pgText(game_dict[lang]['about'], (GAME_WIDTH/2, 50), 50)


pgParagraf(game_dict[lang]['about_text'], (GAME_WIDTH/2-250, 100,
500, GAME_HEIGHT))
pgParagraf(game_dict[lang]['citation_text'], (GAME_WIDTH/2-250, 350,
500, GAME_HEIGHT))

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

- 24 -
def GameHOF():
"""Menu utama dari game"""

teks_hof = adt.algo_makeHOF(game_data['hof'])

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

pgText(game_dict[lang]['highscore'], (GAME_WIDTH/2, 50), 50)

pgParagraf(teks_hof, (GAME_WIDTH/2-250, 100, 500,


GAME_HEIGHT))

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

def GameSetting():
"""Menu utama dari game"""

global game_data
global lang

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

pgText(game_dict[lang]['settings'], (GAME_WIDTH/2, 50), 50)


pgText(game_dict[lang]['sound'], (GAME_WIDTH/2-200,
GAME_HEIGHT/2-50), 40)
pgText(game_dict[lang]['language'], (GAME_WIDTH/2-200,
GAME_HEIGHT/2+50), 40)

- 25 -
# Slider untuk music
game_data['sound'] = pgSlider((GAME_WIDTH/2+200,
GAME_HEIGHT/2-75),
game_data['sound'])
pgMusic()

# Slider untuk bahasa


game_data['lang'] = pgSlider((GAME_WIDTH/2+200,
GAME_HEIGHT/2+25),
game_data['lang'])
if game_data['lang']:
# True berarti memilih Bahasa Inggris
lang = 'en'
else:
lang = 'id'

# Simpan semua setting ke database


adt.data_write(game_data)

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

def GameLogin():
""""bagian user ngasih nama"""
global username
global game_data
global sound
global lang

#font = pg.font.Font(GAME_FONT, 20)


font = pg.font.SysFont("Arial", 18)
input_box = pg.Rect((GAME_WIDTH/2-100, GAME_HEIGHT/2), (200,
32))
color_inactive = pg.Color(139, 195, 74)
color_active = pg.Color(104, 159, 56)
color = color_inactive
active = False
username = ''
done = False

while not done:


for event in pg.event.get():
- 26 -
if event.type == pg.QUIT:
done = True
if event.type == pg.MOUSEBUTTONDOWN:
# If the user clicked on the input_box rect.
if input_box.collidepoint(event.pos):
# Toggle the active variable.
active = not active
username = ''
else:
active = False
# Change the current color of the input box.
color = color_active if active else color_inactive
if event.type == pg.KEYDOWN:
if active:
if event.key == pg.K_RETURN:
GameHomePage()
elif event.key == pg.K_BACKSPACE:
username = username[:-1]
elif len(username) == 11:
username = username
else:
username += event.unicode

gameDisplay.fill((225, 225, 225))


# Render the current text.
txt_surface = font.render(username, True, color)
# Resize the box if the text is too long.
width = max(200, txt_surface.get_width()+10)
input_box.w = width
# Blit the text.
gameDisplay.blit(txt_surface, (input_box.x+5, input_box.y+5))
# Blit the input_box rect.
pg.draw.rect(gameDisplay, color, input_box, 2)

pgImage((GAME_WIDTH/2-100, GAME_HEIGHT/2-250), (200, 200),


"assets/play-h.png")

pgText(game_dict[lang]['enter_name'], (GAME_WIDTH/2,
GAME_HEIGHT/2-40), 18)

"Slider background musik"


pgText(game_dict[lang]['sound'], (GAME_WIDTH/2-80,
GAME_HEIGHT/2+160), 40)
- 27 -
game_data['sound'] = pgSlider((GAME_WIDTH/2+80,
GAME_HEIGHT/2+120), game_data['sound'])
pgMusic()

"Slider bahasa"
pgText(game_dict[lang]['language'], (GAME_WIDTH/2-80,
GAME_HEIGHT/2+90), 40)
game_data['lang'] = pgSlider((GAME_WIDTH/2+80,
GAME_HEIGHT/2+50), game_data['lang'])
if game_data['lang']:
lang = 'en'
else:
lang = 'id'

adt.data_write(game_data)

pg.display.flip()
gameClock.tick(15)

def GameHistory():
"bagian history"
global game_dict

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

pgText(game_dict[lang]['history'], (GAME_WIDTH/2, 50), 50)


pgParagraf(game_dict[lang]['history_text'], (GAME_WIDTH/2-250, 100,
500, GAME_HEIGHT))
pgButton((game_dict[lang]['next']), (GAME_WIDTH/2-100,
GAME_HEIGHT-50), (200, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameInstruction)

# refresh tampilan game


pg.display.update()
- 28 -
gameClock.tick(15)

def GameInstruction():
"""bagian cara bermain"""
global game_dict

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHistory)

pgText(game_dict[lang]['instruction'], (GAME_WIDTH/2, 50), 50)


pgParagraf(game_dict[lang]['instruction_text'], (GAME_WIDTH/2-250,
100, 500, GAME_HEIGHT))
pgButton((game_dict[lang]['play_now']), (GAME_WIDTH/2-100,
GAME_HEIGHT-100), (200, 50),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GamePlay)

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

def GameSecret1():
"""the mathematics behind the game, part 1"""
global game_dict

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

pgText(game_dict[lang]['secret'], (GAME_WIDTH/2, 50), 50)


pgParagraf(game_dict[lang]['secret_text1'], (GAME_WIDTH/2-250, 100,
500, GAME_HEIGHT))

- 29 -
pgButton("1", (100, GAME_HEIGHT/2-130), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret1)
pgButton("2", (100, GAME_HEIGHT/2-60), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret2)
pgButton("3", (100, GAME_HEIGHT/2+10), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret3)
pgButton("4", (100, GAME_HEIGHT/2+80), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret4)

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

def GameSecret2():
"""the mathematics behind the game, part 1"""
global game_dict

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

pgText(game_dict[lang]['secret'], (GAME_WIDTH/2, 50), 50)


pgParagraf(game_dict[lang]['secret_text2'], (GAME_WIDTH/2-250, 100,
500, GAME_HEIGHT))

pgButton("1", (100, GAME_HEIGHT/2-130), (50, 35),


adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret1)
pgButton("2", (100, GAME_HEIGHT/2-60), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret2)
pgButton("3", (100, GAME_HEIGHT/2+10), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret3)
pgButton("4", (100, GAME_HEIGHT/2+80), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
- 30 -
GameSecret4)

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

def GameSecret3():
"""the mathematics behind the game, part 1"""
global game_dict

while True:
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

pgText(game_dict[lang]['secret'], (GAME_WIDTH/2, 50), 50)


pgParagraf(game_dict[lang]['secret_text3'], (GAME_WIDTH/2-250, 100,
500, GAME_HEIGHT))

pgButton("1", (100, GAME_HEIGHT/2-130), (50, 35),


adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret1)
pgButton("2", (100, GAME_HEIGHT/2-60), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret2)
pgButton("3", (100, GAME_HEIGHT/2+10), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret3)
pgButton("4", (100, GAME_HEIGHT/2+80), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret4)

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

def GameSecret4():
"""the mathematics behind the game, part 1"""
global game_dict

while True:
- 31 -
for event in pg.event.get():
if event.type == pg.QUIT:
GameQuit()

gameDisplay.fill(adt.color_use('background'))
pgImage((10, 10), (80, 80), BACK_BUTTON, GameHomePage)

pgText(game_dict[lang]['secret'], (GAME_WIDTH/2, 50), 50)


pgParagraf(game_dict[lang]['secret_text4'], (GAME_WIDTH/2-250, 100,
500, GAME_HEIGHT))

pgButton("1", (100, GAME_HEIGHT/2-130), (50, 35),


adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret1)
pgButton("2", (100, GAME_HEIGHT/2-60), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret2)
pgButton("3", (100, GAME_HEIGHT/2+10), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret3)
pgButton("4", (100, GAME_HEIGHT/2+80), (50, 35),
adt.color_use('button_ok'), adt.color_use('button_ok_hover'),
GameSecret4)

# refresh tampilan game


pg.display.update()
gameClock.tick(15)

#
========================================================
==================================
# Program Utama

global sound
global lang

# music
pg.mixer.init()
pg.mixer.music.load(BG_MUSIC)
pg.mixer.music.play(-1)

# variabel dummy untuk ngecek apakah user ngeklik mouse.


game_mouse_click = False
- 32 -
# variabel yang berisi database program
game_data = adt.data_read()

# initialisasi bahasa dari database


if game_data['lang']:
lang = 'en'
else:
lang = 'id'

#GamePlay()
GameLogin()
input()

- 33 -
conclusion
Actually, there is a the pu 2 n -1. Therefore, the 615 moves!
rule defining the solution zz solution of the original
minimal amount of of the le: puzzle, using 64 disks
movements to find Hanoi it will require: 2n -1 =
Tower is 18446744073709551

- 34 -
- 35 -
- 36 -
- 37 -
- 38 -
- 39 -
- 40 -
- 41 -
- 42 -
- 43 -
- 44 -
- 45 -
- 46 -
- 47 -
- 48 -
- 49 -
- 50 -
- 51 -
- 52 -
- 53 -
- 54 -
- 55 -
- 56 -
- 57 -
- 58 -
- 59 -

You might also like