Torres de Hanói - Versão 2.0
Publicado por Washington Luis de O Santos (última atualização em 23/12/2021)
[ Hits: 2.245 ]
O objetivo deste quebra-cabeça é transferir todos os discos da torre inicial (um de cada vez) para uma das outras torres vazias, sempre respeitando o fato de que, nunca é possível colocar um disco maior em cima de um disco menor. O jogador poderá ir e voltar com os discos em qualquer uma das três torres.
De acordo com um cálculo matemático, o número de movimentos minímos necessários para resolver o quebra-cabeça com 10 discos é igual a 1023.
Esta nova versão do programa, foi feita com o uso de classes/objetos para a criação das torres e também foi ativado o uso do mouse.
Arquivos zipados incluídos no pacote:
LEIAME.txt --> este texto
hanoi-2.0.py --> prg
torresdehanoi.png --> icone do prg
Torres de Hanói.desktop --> edite este arquivo, coloque o caminho onde foi salvo o
prg e o icone e copie ele dentro da pasta "Área de Trabalho" do kde, para criar
um atalho.
torre_de_hanoi.pdf --> doc contando a história do jogo(*)
(*) pego no link: https://www.ibilce.unesp.br/Home/Departamentos/Matematica/labmat/torre_de_hanoi.pdf
Observações:
Para rodar o programa use o Python3
No Debian e/ou Kubuntu (Ubuntu com KDE) instale o pacote " sox " através do apt-get para poder reproduzir os beep's através do " play "
O play é um utilitário de linha de comando (shell), para testar o play direto no terminal copie e cole a linha abaixo:
play --no-show-progress --null -t alsa --channels 1 synth .5 sine 1000
No mais então... FELIZ NATAL, divirtam-se, deem um joinha se gostaram e vejam os outros
programas no link: https://www.vivaolinux.com.br/~WashingtonLuis/scripts/
Por: Washington Luis de O Santos
#!/usr/bin/env python3
# -*- coding:UTF-8 -*-
#coding: cp1252
#coding: latin1
'''
Torres de Hanoi version 2.0 - Program
Copyright (c) 2002-2004 Washington Luis de O. Santos
< owashington[arroba]terra.com.br >
Este programa, primeiro, foi desenvolvido em clipper 5.2 por mim em novembro de
1994 e agora foi convertido e adaptado para o python3 com o uso do módulo 'curses'
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Taubaté - SP, 17 de dezembro de 2020
'''
import sys
import time
import curses
import os
from random import shuffle, randint
# janela principal
screen = curses.initscr()
# tamanho da janela principal
screen_x, screen_y = screen.getmaxyx()
if screen_x < 24 or screen_y < 81:
screen.clear()
curses.endwin()
print('Tamanho atual do terminal: {} x {}'.format(screen_x, screen_y))
print('\nO seu terminal tem que ter no minimo 24 linhas por 81 colunas.\n')
print('Reajuste o tamanho do seu terminal para poder jogar... \n')
sys.exit(0)
screen.keypad(True)
screen.notimeout(False)
curses.cbreak()
# Não retorna caracteres na tela
curses.noecho()
# esconde o cursor do mouse e do terminal
curses.curs_set(0)
# move o cursor para a posicao 0,0
#screen.move(0,0)
# limpa a tela
screen.clear()
#screen.set_title('T O R R E S D E H A N O I')
curses.mousemask(curses.ALL_MOUSE_EVENTS)
# atualiza a tela automaticamente mais causa perda de performance
# pode ser usado no lugar das chamadas da funcao screen.refresh()
#screen.immedok(True)
# iniciando cores
curses.start_color()
curses.use_default_colors()
# Define cor da tela de abertura (autor)
curses.init_pair( 1, curses.COLOR_GREEN, curses.COLOR_WHITE)
# Define cor dos textos (autor) e box e da Base das Torres
curses.init_pair( 2, curses.COLOR_BLUE, curses.COLOR_WHITE)
# Define cor da Versão (autor) e sombra
curses.init_pair( 3, curses.COLOR_BLACK, curses.COLOR_WHITE)
# Define cor da Barra de Status
curses.init_pair( 4, curses.COLOR_WHITE, curses.COLOR_CYAN)
# Define cor da mensagem de error (box)
curses.init_pair( 5, curses.COLOR_RED, curses.COLOR_WHITE)
# Define cor da parte de baixo da tela
curses.init_pair( 6, curses.COLOR_WHITE, curses.COLOR_BLUE)
# Define cor do botão acionado
curses.init_pair( 7, curses.COLOR_BLUE, curses.COLOR_WHITE)
# Define cor da sombra do botão acionado (apaga)
curses.init_pair( 8, curses.COLOR_WHITE, curses.COLOR_BLUE)
# Define cor do botão NÃO acionado
curses.init_pair( 9, curses.COLOR_BLACK, curses.COLOR_WHITE)
# Define cor da sombra do botão NÃO acionado (cria)
curses.init_pair(10, curses.COLOR_BLACK, curses.COLOR_BLUE)
# Define as cores dos discos e embaralha (shuffle)
cor = [0,1,2,3,4,5,6,0,1,2,3]
shuffle(cor)
for i in range(11):
curses.init_pair(i+11, cor[i], curses.COLOR_WHITE)
# Define Constantes usadas como caracteres especiais
# Single-line
# Chr( 218 ) + Chr( 196 ) + Chr( 191 ) ┌ ─ ┐
# Chr( 179 ) + Chr( 32 ) + Chr( 179 ) │ │
# Chr( 192 ) + Chr( 196 ) + Chr( 217 ) └ ─ ┘
c_032 = chr( 32) # espaço
c_168 = chr(9608)
c_177 = chr(9619) # bloco
c_178 = chr(9618)
c_179 = chr(9474)
c_191 = chr(9488)
c_192 = chr(9492)
c_196 = chr(9472)
c_217 = chr(9496)
c_218 = chr(9484)
c_219 = chr(9604)
c_223 = chr(9600)
# Define Constantes
K_CTRL_Q = 17
K_ESC = 27
K_1 = 49
K_2 = 50
K_3 = 51
class Box:
def Fill(lt,ce,lb,cd, cor):
#Desenha uma caixa sem bordas e sem sombras
for x in range(lt,lb+1):
screen.addstr(x, ce, c_032 * (cd-ce+1), curses.color_pair(cor))
def Display(lt,ce,lb,cd,cor):
#Desenha uma caixa com bordas e sem sombras
__class__.Fill(lt,ce,lb,cd,cor)
screen.addstr(lt, ce, c_196 * (cd-ce), curses.color_pair(cor))
screen.addstr(lt, ce, c_218, curses.color_pair(cor))
screen.addstr(lt, cd, c_191, curses.color_pair(cor))
screen.addstr(lb, ce, c_196 * (cd-ce), curses.color_pair(cor))
screen.addstr(lb, ce, c_192, curses.color_pair(cor))
screen.addstr(lb, cd, c_217, curses.color_pair(cor))
for x in range(lt+1,lb):
screen.addstr(x, ce, c_179, curses.color_pair(cor))
screen.addstr(x, cd, c_179, curses.color_pair(cor))
def Shadow(lt,ce,lb,cd,cor):
#Desenha uma caixa com bordas e com sombras
__class__.Display(lt,ce,lb,cd,cor)
#Desenha a Sombra da Caixa
for x in range(lt+1,lb+1):
screen.addstr(x, cd+1, c_032, curses.color_pair(0))
screen.addstr(lb+1, ce+1, c_032 * (cd-ce+1), curses.color_pair(0))
def Beep(duration = .1, frequency = 850):
#curses.beep()
#os.system('beep -f %s -l %s' % (frequency,duration))
#os.system('beep -f 555 -l 460')
# Observação:
# O play é um utilitário de linha de comando (shell), para poder usa-lo
# instale o Pacote sox atraves do apt-get no Debian
# Para testar o play direto no bash copie e cole a linha abaixo:
#play --no-show-progress --null -t alsa --channels 1 synth .5 sine 1000
try:
os.system('play --no-show-progress --null -t alsa --channels 1 synth %s sine %f' % (duration, frequency))
except:
pass
def pause(tempo):
# Atualiza a tela
screen.refresh()
# Pausa por um tempo
time.sleep(tempo)
# Limpa qualquer outra coisa que o usuário tenha digitado
curses.flushinp()
def autor():
# Mostra a tela de abertura
# Obs: As 'fontes' usadas foram criadas através do programa
# figlet - http://www.figlet.org/
# digite o cmd abaixo para ver as fontes disponiveis
# showfigfonts
# digite o cmd abaixo para criar o arquivo com o texto
# figlet -f big "Torres" >> hanoi.txt
# figlet -f big "de" >> hanoi.txt
# figlet -f big "Hanói" >> hanoi.txt
Box.Shadow(1,10,20,68, 2)
screen.addstr( 2, 22, ' _____', curses.color_pair(1) | curses.A_BOLD)
screen.addstr( 3, 22, '|_ _|__ _ __ _ __ ___ ___', curses.color_pair(1) | curses.A_BOLD)
screen.addstr( 4, 22, ' | |/ _ \\| \'__| \'__/ _ \\/ __|', curses.color_pair(1) | curses.A_BOLD)
screen.addstr( 5, 22, ' | | (_) | | | | | __/\\__ \\', curses.color_pair(1) | curses.A_BOLD)
screen.addstr( 6, 22, ' |_|\\___/|_| |_| \\___||___/', curses.color_pair(1) | curses.A_BOLD)
screen.addstr( 7, 22, ' _', curses.color_pair(1) | curses.A_BOLD)
screen.addstr( 8, 22, ' __| | ___', curses.color_pair(1) | curses.A_BOLD)
screen.addstr( 9, 22, ' / _` |/ _ \\', curses.color_pair(1) | curses.A_BOLD)
screen.addstr(10, 22, ' | (_| | __/', curses.color_pair(1) | curses.A_BOLD)
screen.addstr(11, 22, ' _ _ \\__,_|\\___| _', curses.color_pair(1) | curses.A_BOLD)
screen.addstr(12, 22, ' | | | | __ _ _ __ ___ (_)', curses.color_pair(1) | curses.A_BOLD)
screen.addstr(13, 22, ' | |_| |/ _` | \'_ \\ / _ \\| |', curses.color_pair(1) | curses.A_BOLD)
screen.addstr(14, 22, ' | _ | (_| | | | | (_) | |', curses.color_pair(1) | curses.A_BOLD)
screen.addstr(15, 22, ' |_| |_|\\__,_|_| |_|\\___/|_|', curses.color_pair(1) | curses.A_BOLD)
screen.addstr(10, 52, 'Versão 2.0', curses.color_pair(3))
screen.addstr(17, 15, 'Autor: Washington Luis de Oliveira Santos', curses.color_pair(2))
screen.addstr(18, 15, 'End. : Av. Campinas, 749 - Chácara do Visconde', curses.color_pair(2))
screen.addstr(19, 28, 'Taubaté - São Paulo', curses.color_pair(2))
def ENCERRA():
#Restaura a cor do terminal
screen.refresh()
screen.clear()
screen.keypad(False)
curses.nocbreak()
curses.echo()
curses.endwin()
sys.exit(0)
def inkey():
while True:
try:
key = screen.getch()
if key == curses.KEY_MOUSE:
# botão esquerdo do mouse foi pressionado
_, mx, my, _, _ = curses.getmouse()
#screen.addstr (22, 1,"my = %i | mx = %i" % (my, mx))
# Verifica em que posicao foi pressionado
if (6 < my < 22) and ( 2 < mx < 24):
return 1
elif (6 < my < 22) and (28 < mx < 50):
return 2
elif (6 < my < 22) and (54 < mx < 76):
return 3
except curses.error:
pass
if key in (K_ESC, K_CTRL_Q):
# encerra o programa
Box.Shadow( 8,23,14,55, 5)
screen.addstr(11, 30, 'Jogo abortado...', curses.color_pair(5))
pause(5)
ENCERRA()
elif key in (curses.KEY_F1, ord('h'), ord('H')):
# help acionado
autor()
screen.addstr(23, 0, c_032 * 80, curses.color_pair(4))
screen.addstr(23, 1, 'Tecle algo para sair...', curses.color_pair(4) | curses.A_BOLD)
# O comando abaixo faz com que a chamada a getch() retorne depois
# de um tempo simulando a função inkey(<tempo>) do CLIPPER
curses.halfdelay(7)
while True:
key = screen.getch()
if key != -1:break
# Fica piscando a tela (mudando de cor)
curses.init_pair( 1, randint(0, 7), curses.COLOR_WHITE)
screen.refresh()
return 0
elif key == K_1:
return 1
elif key == K_2:
return 2
elif key == K_3:
return 3
class Torre:
def __init__(self):
# cria uma torre sem os Discos
self.nd = 0
# Se quiser ver os discos com 'caracteres graficos'
# mude a linha IF abaixo de 0 para 1
if 0:
self.torre = [[1, (c_032 * 10) + c_178]] * 11
else:
self.torre = [[1, (c_032 * 10) + '|']] * 11
def disc_fill(self):
# cria uma torre com os Discos
self.nd = 10
self.torre[ 0] = ([ 1, ' | '])
self.torre[ 1] = ([ 2, ' #|# '])
self.torre[ 2] = ([ 3, ' ##|## '])
self.torre[ 3] = ([ 4, ' ###|### '])
self.torre[ 4] = ([ 5, ' # FELIZ # '])
self.torre[ 5] = ([ 6, ' ## NATAL ## '])
self.torre[ 6] = ([ 7, ' ######|###### '])
self.torre[ 7] = ([ 8, ' ## GENTILEZA ## '])
self.torre[ 8] = ([ 9, ' ### GERA ### '])
self.torre[ 9] = ([10, ' #### GENTILEZA #### '])
self.torre[10] = ([11, '##########|##########'])
# Se quiser ver os discos com 'caracteres graficos'
# mude a linha IF abaixo de 0 para 1
if 0:
for x in range(11):
self.torre[x][1] = self.torre[x][1].replace('#', c_177)
self.torre[x][1] = self.torre[x][1].replace('|', c_178)
def disc_entra(self, de):
self.torre[10 - self.nd] = de
self.nd += 1
def disc_sai(self):
self.nd -= 1
aux = self.torre[10 - self.nd]
self.torre[10 - self.nd] = self.torre[0]
return aux
def disc_size(self):
if self.nd == 0:
return(self.torre[self.nd][0])
else:
return(self.torre[11 - self.nd][0])
def disc_total(self):
return self.nd
def disc_show(self, col):
for x in range(11):
screen.addstr(x+7, col, self.torre[x][1], curses.color_pair(self.torre[x][0]+10))# | curses.A_BOLD)
def bt_solto(n_bt):
'''
desenha o botão não acionado
n_bt = numero do botão
'''
posicao = {1:(10,11,17), 2:(36,37,43), 3:(62,63,69)}
screen.addstr(20, posicao[n_bt][0], '{:^7}'.format(n_bt), curses.color_pair(9))
# Cria a sombra do botão
screen.addstr(21, posicao[n_bt][1], c_223 * 7, curses.color_pair(10))
screen.addstr(20, posicao[n_bt][2], c_219 , curses.color_pair(10))
def bt_press(n_bt):
'''
desenha o botão acionado
n_bt = numero do botão
'''
posicao = {1:(10,11), 2:(36,37), 3:(62,63)}
screen.addstr(20, posicao[n_bt][1], '{:^7}'.format(n_bt), curses.color_pair(7) | curses.A_BOLD)
# Apaga a sombra do botão
screen.addstr(20, posicao[n_bt][0], c_032 , curses.color_pair(8))
screen.addstr(21, posicao[n_bt][1], c_032 * 7, curses.color_pair(8))
def display_tela():
#Cria um quadro na tela com char azul e fundo branco
Box.Display(0,0,18,79, 2)
#Escreve o titulo
screen.addstr( 2,24, 'T O R R E S D E H A N O I', curses.color_pair(2) | curses.A_BOLD)
screen.addstr( 3,45, 'ver. 2.0', curses.color_pair(2))
#Desenha a Torre e os discos
torre[1].disc_show( 3)
torre[2].disc_show(29)
torre[3].disc_show(55)
#Desenha a base
screen.addstr(18, 0, c_178 * 80, curses.color_pair(2))
#Apaga/Muda a cor na parte de baixo da tela
Box.Fill(19,0,23,79,10)
# Desenha os botoes
bt_solto(1)
bt_solto(2)
bt_solto(3)
screen.addstr(23, 0, c_032 * 80, curses.color_pair(4))
def main():
while True:
global torre
torre = {}
# Determina a torre que contera os discos e embaralha
ti = [1, 2, 3]
shuffle(ti)
# Conta o nº de movimentos
n_mov = 0
# Cria as torres sem os discos
torre[ti[0]] = Torre()
torre[ti[1]] = Torre()
torre[ti[2]] = Torre()
# Preenche a torre com os discos
torre[ti[0]].disc_fill()
#Imprimi a tela de abertura
display_tela()
autor()
for _ in range(10):
# Fica piscando a tela (mudando de cor)
curses.init_pair(1, randint(0, 7), curses.COLOR_WHITE)
pause(.7)
while True:
display_tela()
# mostra o nº de discos em cada torre (só pra depuração)
#screen.addstr(21, 8, 'tot disc {}'.format(torre[1].disc_total()), curses.color_pair(6))
#screen.addstr(21, 34, 'tot disc {}'.format(torre[2].disc_total()), curses.color_pair(6))
#screen.addstr(21, 60, 'tot disc {}'.format(torre[3].disc_total()), curses.color_pair(6))
# mostra o tamanho do disco do topo da torre (só pra depuração)
#screen.addstr(22, 9, 'tamanho {}'.format(torre[1].disc_size()), curses.color_pair(6))
#screen.addstr(22, 35, 'tamanho {}'.format(torre[2].disc_size()), curses.color_pair(6))
#screen.addstr(22, 61, 'tamanho {}'.format(torre[3].disc_size()), curses.color_pair(6))
screen.addstr(23, 62, 'Movimentos: {:5}'.format(n_mov), curses.color_pair(4) | curses.A_BOLD)
#Pede para o usuario fazer o movimento
screen.addstr(23, 1, 'Entre com o nº da torre de origem ', curses.color_pair(4) | curses.A_BOLD)
origem = inkey()
if origem == 0: continue
bt_press(origem)
if torre[origem].disc_total() == 0:
# Torre vazia
Beep()
screen.addstr(23, 1, 'Esta torre esta vazia... ', curses.color_pair(4) | curses.A_BOLD)
pause(2)
continue
screen.addstr(23, 1, 'Entre com o nº da torre de destino', curses.color_pair(4) | curses.A_BOLD)
destino = inkey()
if destino == 0: continue
bt_press(destino)
pause(.1)
if destino == origem:
Beep()
continue
# Verifica se o movimento e valido
if (torre[destino].disc_total() > 0) and (torre[origem].disc_size() > torre[destino].disc_size()):
Beep()
screen.addstr(23, 1, 'Movimento ilegal... ', curses.color_pair(4) | curses.A_BOLD | curses.A_BLINK)
pause(5)
continue
# Move o disco de uma torre para a outra
torre[destino].disc_entra(torre[origem].disc_sai())
n_mov += 1
#Verifica se chegou no fim do jogo
if torre[ti[1]].disc_total() == 10 or torre[ti[2]].disc_total() == 10:
display_tela()
screen.addstr(23, 62, 'Movimentos: {:5}'.format(n_mov), curses.color_pair(4) | curses.A_BOLD)
Box.Shadow(8,23,14,55, 2)
screen.addstr( 9, 26, 'Meus Parabéns...', curses.color_pair(2) | curses.A_BOLD)
screen.addstr(11, 26, 'Você conseguiu!!!', curses.color_pair(2) | curses.A_BOLD)
# Verifica se quer brincar novamente
screen.addstr(13, 26, 'Quer tentar outra vez? (S/N)', curses.color_pair(2))
pause(5)
key = screen.getch()
if key in (ord('S'), ord('s')):
break
else:
ENCERRA()
if __name__ == '__main__':
try:
curses.wrapper(main())
except KeyboardInterrupt:
ENCERRA()
Just Do It - XML Generic Editor
Cirurgia para acelerar o openSUSE em HD externo via USB
Void Server como Domain Control
Modo Simples de Baixar e Usar o bash-completion
Monitorando o Preço do Bitcoin ou sua Cripto Favorita em Tempo Real com um Widget Flutuante
Como fazer a conversão binária e aplicar as restrições no Linux
Como quebrar a senha de um servidor Linux Debian
Como bloquear pendrive em uma rede Linux
Um autoinstall.yaml para Ubuntu com foco em quem vai fazer máquina virtual
Instalar GRUB sem archinstall no Arch Linux em UEFI Problemático
Fiz uma pergunta no fórum mas não consigo localizar [RESOLVIDO] (21)









