Torres de Hanói - Versão 2.0
Publicado por Washington Luis de O Santos (última atualização em 23/12/2021)
[ Hits: 1.637 ]
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()
Veja a versão das principais distrubuições.
Exercício com números randômicos - randint
Melhorando o tempo de boot do Fedora e outras distribuições
Como instalar as extensões Dash To Dock e Hide Top Bar no Gnome 45/46
E a guerra contra bots continua
Tradução do artigo do filósofo Gottfried Wilhelm Leibniz sobre o sistema binário
Conheça o firewall OpenGFW, uma implementação do (Great Firewall of China).
Instalando o FreeOffice no LMDE 6
Anki: Remover Tags de Estilo HTML de Todas as Cartas
Colocando uma opção de redimensionamento de imagem no menu de contexto do KDE
Não consigo acessar os modos de desempenho (2)
Ubuntu — tentando iniciar o windows? (0)
[Shell Script] Script para desinstalar pacotes desnecessários no OpenSuse
[Shell Script] Script para criar certificados de forma automatizada no OpenVpn
[Shell Script] Conversor de vídeo com opção de legenda
[C/C++] BRT - Bulk Renaming Tool
[Shell Script] Criação de Usuarios , Grupo e instalação do servidor de arquivos samba