Obrigado a Julio Marchi pelo espaço cedido na MSX All |
||||||||
English Jogo de estratégia, onde um jogador deve derrubar toda a frota do adversário antes que o adversário elimine toda a sua frota. Há diversas variações do jogo, tanto o tamanho do tabuleiro, quanto à quantidade de navios e seus formatos. Nessa versão, o tabuleiro possui o tamanho de 10x10, onde as coordenadas são representadas por uma letra para o eixo horizontal e um número para o eixo vertical. Um exemplo de coordenada seria "B7". Além disso, possui 10 navios com os seguintes tipos, formatos e quantidades de cada um: Tipo | Formato | Quantidade -------------+---------+----------- Porta-aviões | PPPP | 1 Cruzador | CCC | 2 Hidro-avião | HH | 3 Submarino | S | 4 Inicialmente, cada jogador irá posicionar sua frota dentro do tabuleiro, respeitando o formato do navio e sem exceder as dimensões do tabuleiro. Válido Inválido ..... ..... .HH.. ....H Não poderá haver superposição de navios, além de que os navios deverão estar distantes entre si, pelo menos, uma casa em qualquer direção. Válido Inválido Inválido ..... ..... .. O objetivo do jogo é encontrar todos os navios do adversário, antes que ele descubra os seus. O jogo é composto de dois tabuleiros para cada jogador: um para anotar o posicionamento de sua própria frota, e outro para anotar o que for sendo descoberto do adversário. Cada jogador informa uma coordenada ao adversário e ele responde "água", se nenhum navio estiver naquela posição, ou "acertou um ...", caso haja encontrado uma parte de um navio. Rotina que desenha o tabuleiro O jogo será desenhado na screen 1 para maior legibilidade. 5 ' Rem MarMSX 2018 10 COLOR 15,0,0:SCREEN 1:WIDTH 32:KEY OFF ... 200 ' 201 ' Desenha tabuleiro 202 ' 210 DIM PX(2):PX(1)=0:PX(2)=18 220 FOR I=1 TO 2 230 LOCATE PX(I)+2,0:?"ABCDEFGHIJ" 240 NEXT I 250 FOR Y=1 TO 10:FOR I=1 TO 2 260 LOCATE PX(I),Y:PRINT RIGHT$(" "+STR$(Y),2);".........." 270 NEXT I,Y 275 LOCATE 4,12:PRINT"Voce":LOCATE 24,12:PRINT"Eu" 280 LOCATE 0,17 290 FOR I=1 TO 5 300 LOCATE 0:READ A$:PRINT A$; 310 LOCATE 15:READ A$:PRINT A$; 320 LOCATE 25:READ A$:PRINT A$ 325 IF I=1 THEN PRINT 330 NEXT I 340 RETURN ... 2000 ' 2001 ' Dados do jogo 2002 ' ... 2020 DATA Navio, Formato, Total, Porta-avioes, PPPP, 1,Cruzador, CCC, 2, Hidro-aviao, HH, 3, Submarino, S, 4 Rotina que posiciona os navios do jogador e computador Há duas maneiras de posicionar os navios do jogador no tabuleiro: a primeira, criando uma rotina que permita o próprio jogador posicionar seus navios; a segunda, criando uma rotina que faça o posicionamento automaticamente para ele. Nesse jogo, iremos posicionar automaticamente os navios do jogador e do computador. Desse modo, podemos aproveitar a mesma rotina para criar ambos os jogos. Para maior facilidade de acesso aos tabuleiros, vamos criar uma matriz tridimensional com os tabuleiros "superpostos". Assim, a matriz "TJ" teria as dimensões 10x10x2. 20 BT=10:DIM TJ(BT,BT,2):DIM N$(4)Obs: o tamanho do tabuleiro é indicado pela variável BT, que dá maior flexibilidade à rotina de criação do jogo. Por exemplo, quando desejássemos acessar a posição 5,4 do jogador, faríamos TJ(5,4,1) e para a mesma posição do computador, faríamos TJ(5,4,2), ou seja, basta indicar o valor "1" ou "2" da última coordenada de "TJ" para escolher um dos tabuleiros. O vetor "N$" irá armazenar os símbolos dos 4 tipos de navios:
2010 DATA P,4,1,C,3,2,H,2,3,S,1,4Obs: como esses dados serão lidos primeiro que os dados utilizados pela rotina que desenha o tabuleiro, eles deverão aparecer primeiro na listagem do programa. Uma vez tendo todas as informações necessárias, podemos enfim posicionar os navios nos tabuleiros. Estratégia básica para posicionar os navios: var TJ : vetor[1..10, 1..10, 1..2] de inteiro; N : string; C,T,X,Y : inteiro; i, it, p : inteiro; para i ← 1 até 4 faça leia(N, C, T); para it ← 1 até T faça para p ← 1 até 2 faça X ← aleatorio(10-C+1); Y ← aleatorio(10); insira_navio(i, X, Y); fim_para fim_para fim_para Onde:
Como garantir que o valor aleatório gerado não faz com que o objeto de comprimento "C" não ultrapasse os limites do tabuleiro? Para objetos de tamanho igual a 1, bastaria criar um número aleatório de 1 a 10 e problema estaria resolvido. Entretanto, os objetos possuem comprimento diferente de 1 no eixo X. Para contornar esse problema, podemos criar um pivô na menor coordenada de X do objeto e delimitar o valor máximo de X em relação ao comprimento "C". Pivô do navio em verde Para um tabuleiro de 10x10, os valores aleatórios seriam calculados da seguinte maneira: Y: mínimo=1, máximo=10 X: mínimo=1, máximo=10-C+1Para um cruzador de comprimento "C" igual a 3, teríamos o valor em X variando de 1 a 8, e o valor em Y de 1 a 10, o que corresponde ao retângulo vermelho da figura anterior. Escolhidas as coordenadas X e Y, colocaremos o código do navio na matrix "TJ". Entretanto, devemos escrever o código em "C" posições consecutivas. Assim, temos: para ic ← 0 até C-1 faça TJ[X+ic,Y,P] ← i; fim_paraObs: uma vez que o tabuleiro é de valores inteiros, vamos armazenar o código do navio e não a string. O código em Basic fica: 400 ' 401 ' Cria jogo 402 ' 410 FOR I=1 TO 4 413 LOCATE 0,2:PRINT"Criando:";INT(100*(I-1)/4);"%" 415 READ N$(I),C,T 420 FOR IT=1 TO T 425 FOR P=1 TO 2 430 X=INT((BT-C+1)*RND(-TIME))+1 435 Y=INT(BT*RND(-TIME))+1 440 IF I>1 THEN GOSUB 500 ELSE 460 445 IF FV=1 THEN 460 450 X=X+1:IF X+C-1>BT THEN X=1:Y=Y+1 453 IF Y>BT THEN Y=1 455 GOTO 440 460 FOR IC=0 TO C-1:TJ(X+IC,Y,P)=I:NEXT 465 NEXT P,IT,I 470 RETURN As linhas 440-455 irão tratar da superposição dos objetos. Para o primeiro objeto, um "porta-aviões", não é necessário rodá-la, pois o tabuleiro está vazio. A rotina que trata especificamente da superposição está na linha 500. Ela retorna o valor em "FV" igual a 0, caso o objeto atual superponha algum objeto existente. Caso haja superposição, podemos adotar duas estratégias diferentes:
Rotina que analisa a superposição de objetos Uma vez obtidas as coordenadas X,Y do pivô do navio, devemos verificar se ele se sobrepõe a um objeto existente. Lembrando que não poderemos ter objetos com distância menor que uma casa, deveremos verificar se na área do retângulo em torno do objeto existe algum navio. O exemplo da figura acima mostra a área de verificação (em verde) para um cruzador. Código: 500 ' 501 ' Verifica superposicao 502 ' 510 FV=1 520 FOR VY=Y-1 TO Y+C:FOR VX=X-1 TO X+C 530 IF VX<1 OR VX>BT OR VY<1 OR VY>BT THEN 550 540 IF TJ(VX,VY,P)<>0 THEN FV=0:RETURN 550 NEXT VX,VY 560 RETURN Jogo do jogador Esta rotina apresenta o tabuleiro do jogador logo que a tela é desenhada. 1000 ' 1001 ' Mostra jogo do jogador 1002 ' 1010 FOR Y=1 TO 10:FOR X=1 TO 10 1020 LOCATE X+1,Y:IF TJ(X,Y,1)<>0 THEN PRINT N$(TJ(X,Y,1)) ELSE PRINT " " 1025 'LOCATE X+19,Y:IF TJ(X,Y,2)<>0 THEN PRINT N$(TJ(X,Y,2)) ELSE PRINT "X" 1030 NEXT X,Y 1040 RETURN Loop principal Após posicionar os navios e desenhar a tela, criamos o loop principal que irá controlar as ações do jogador e do computador. 30 PRINT "Batalha Naval - MarMSX 2018" 40 GOSUB 400:CLS:GOSUB 200:GOSUB 1000 50 CX=20:CY=1:PJ=0:PC=0 100 ' 101 ' Loop principal 102 ' 110 LOCATE CX,CY:A$=INPUT$(1):A=ASC(A$) 120 IF A=29 THEN CX=CX-1:IF CX<20 THEN CX=20 130 IF A=28 THEN CX=CX+1:IF CX>29 THEN CX=29 140 IF A=30 THEN CY=CY-1:IF CY<1 THEN CY=1 150 IF A=31 THEN CY=CY+1:IF CY>10 THEN CY=10 160 IF A=32 THEN GOSUB 600:IF V=1 THEN GOSUB 650 170 GOTO 110 O código controla o movimento do cursor no mapa do computador visto pelo jogador. A barra de espaços abre uma casa do tabuleiro do computador, revelando o que havia ali. A rotina (linha 600) retorna um flag "V" informando se esse procedimento é valido. Se for, passa a vez para o computador jogar (linha 650). Analisa a jogada Esta rotina primeiramente verifica se a casa escolhida pelo jogador não havia sido aberta antes. Isto é feito, verificando se naquela coordenada da tela existe um ponto ".". Ao abrir a casa, ela imprime vazio " " caso o valor da coordenada X,Y da matriz código seja 0. Caso seja diferente de 0, ela imprime o símbolo relativo ao código encontrado (N$). Além disso, incrementa o valor de "PJ", informando que um objeto foi encontrado para os "pontos do jogador". Ao atingir 20, o jogador ganha a partida. 600 ' 601 ' Analisa o comando do jogador 602 ' 610 V=1:IF VPEEK(6144+CX+CY*32)<>46 THEN V=0:RETURN 620 LOCATE CX,CY:IF TJ(CX-19,CY,2)<>0 THEN PRINT N$(TJ(CX-19,CY,2)):PJ=PJ+1 ELSE PRINT " " 630 IF PJ=20 THEN LOCATE 0,14:PRINT"** Voce venceu o jogo !! **":END 640 RETURN O computador joga A coordenada é escolhida aleatoriamente. Então, ele verifica se a posição escolhida já havia sido aberta. Em caso positivo, ele passa para a próxima posição do tabuleiro até encontrar uma posição vazia. Diferente do outro tabuleiro, esse é marcado com um "X" nas casas visitadas. Caso encontre um objeto, incrementa "PC", o placar do computador. 650 ' 651 ' O computador joga 652 ' 660 X=INT(BT*RND(-TIME))+1 665 Y=INT(BT*RND(-TIME))+1 670 IF VPEEK(6144+(X+1)+Y*32)<>88 THEN 685 675 X=X+1:IF X>10 THEN Y=Y+1:X=1:IF Y>10 THEN Y=1 680 GOTO 670 685 LOCATE X+1,Y:PRINT"X":IF TJ(X,Y,1)<>0 THEN PC=PC+1 690 IF PC=20 THEN LOCATE 0,14:PRINT"** Eu venci o jogo !! **":END 695 RETURN Importante: a consulta à tabela do adversário poderá ser somente realizada a partir do palpite feito pelo jogador ou pelo computador em uma coordenada X,Y. Qualquer outro acesso fora do palpite seria "roubar" o adversário. Por isso, as linhas 670 da rotina do computador e 610 da rotina do jogador fazem acesso à informação visualizada na tela. A rotina do computador (linhas 650-695) é bem simples e sempre dá "um tiro no escuro", uma vez que não modifica seu comportamento após identificar um navio e calcula X e Y sempre aleatoriamente. Podemos modificar isso, fazendo com que o computador busque pelas casa adjacentes, sempre que encontrar um objeto de tamanho maior que 1. O primeiro passo é criar um vetor "N" para armazenar o tamanho de cada navio, pois agora será necessário obter essa informação. 20 BT=10:DIM TJ(BT,BT,2):DIM N$(4) Agora, serão duas opções para o computador: uma é a coordenada X,Y obtida aleatoriamente, e a outra coordenada obtida através de uma perseguição ao navio encontrado. Um flag deverá ser criado, de forma que indique à rotina se ela deverá escolher aleatoriamente uma posição, ou continuar a busca pelo objeto encontrado. 50 CX=20:CY=1:PJ=0:PC=0 O código fica: 655 IF OE=1 THEN 690 656 ' Rotina aleatoria 660 X=INT(BT*RND(-TIME))+1 665 Y=INT(BT*RND(-TIME))+1 670 IF VPEEK(6144+(X+1)+Y*32)<>88 THEN 685 675 X=X+1:IF X>10 THEN Y=Y+1:X=1:IF Y>10 THEN Y=1 680 GOTO 670 685 IF TJ(X,Y,1)<>0 THEN OE=1:CR=N(TJ(X,Y,1)):C=CR:DX=1 690 LOCATE X+1,Y:PRINT"X":IF TJ(X,Y,1)<>0 THEN PC=PC+1 695 IF PC=20 THEN LOCATE 0,14:PRINT"** Eu venci o jogo !! **":END 700 IF OE=0 THEN RETURN 705 ' Rotina de perseguicao 710 IF TJ(X,Y,1)=0 THEN DX=-DX:GOTO 760 720 CR=CR-1:IF CR=0 THEN OE=0:RETURN 730 X=X+DX 740 IF X<1 OR X>10 THEN DX=-DX:GOTO 760 750 IF VPEEK(6144+(X+1)+Y*32)<>88 THEN 770 ELSE DX=-DX 760 X=X+DX:IF VPEEK(6144+(X+1)+Y*32)=88 THEN 760 770 RETURN A linha 685 analisa se a posição X,Y aleatória é um navio. Se for, seta o flag "OE", indica o comprimento do objeto em "CR" (comprimento restante) e ajusta o deslocamento inicial "DX" como sendo positivo. A partir das rodadas restantes, a rotina de perseguição (linha 705) é executada até que CR=0. Se o alvo foi água, modifique o sentido "DX" da perseguição. Desloque X até que encontre uma posição não visitada (linha 760). Na rodada atual, o valor da próxima posição de X já é calculado. Se a posição X já tiver sido visitada, mude o sentido e desloque X até que encontre uma posição não visitada (linha 760). Situação 1: Obs: o retângulo verde corresponde às coordenadas X,Y da jogada atual, e o retângulo cinza claro aos X,Y futuros. Situação 2: A linha 690 corresponde a um palpite do computador tanto da rotina aleatória, como da rotina de perseguição. Podemos também sinalizar o entorno do objeto encontrado, para minimizar o esforço do computador. 720 CR=CR-1:IF CR=0 THEN OE=0: Download: batalha.zip - jogo no formato Basic e Basic ASCII. |
||||||||