Torres de Hanói - Versão 2.0
Publicado por Washington Luis de O Santos (última atualização em 23/12/2021)
[ Hits: 2.164 ]
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()
Sugestão aleatória de filmes e séries para assistir por streaming
Cup - um gerenciador de notas simples
Unescape de caracteres especiais ISO-8859-1
Just Do It - XML Generic Editor
IA Turbina o Desktop Linux enquanto distros renovam forças
Como extrair chaves TOTP 2FA a partir de QRCODE (Google Authenticator)
Linux em 2025: Segurança prática para o usuário
Desktop Linux em alta: novos apps, distros e privacidade marcam o sábado
IA chega ao desktop e impulsiona produtividade no mundo Linux
Atualizando o Fedora 42 para 43
Como saber se o seu e-mail já teve a senha vazada?
Como descobrir se a sua senha já foi vazada na internet?
Programa fora de escala na tela do pc [RESOLVIDO] (43)
\Boot sem espaço em disco (Fedora KDE Plasma 42) (5)
Preciso recuperar videos *.mp4 corrompidos (0)









