Torres de Hanói - Versão 2.0

Publicado por Washington Luis de O Santos (última atualização em 23/12/2021)

[ Hits: 1.841 ]

Download Torres de Hanói.zip




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

  



Esconder código-fonte

#!/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 ) &#9484; &#9472; &#9488;
# Chr( 179 ) + Chr(  32 ) + Chr( 179 ) &#9474;   &#9474;
# Chr( 192 ) + Chr( 196 ) + Chr( 217 ) &#9492; &#9472; &#9496;

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()

Scripts recomendados

Números Complexos

Desenha poligonos

Teclado Virtual

Troca de wallpaper temporizado para LXDE

Unescape de caracteres especiais ISO-8859-1


  

Comentários
[1] Comentário enviado por maurixnovatrento em 09/01/2022 - 11:42h


Achei o script bem organizado.

___________________________________________________________
[code]Conhecimento não se Leva para o Túmulo.
https://github.com/mxnt10 [/code]


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts