Capa Relatório de Avaliação Intercalar do 1º Trabalho Prático da Disciplina de Programação em Lógica Jogo Escolhido: Elementos do Grupo: Daniel Augusto Gama Castro Silva: Vasco Hugo Vinhas Gonçalves Moreira: ei01083@fe.up.pt vasco.vinhas@fe.up.pt Página 1 de 13
Resumo & Introdução Resumo O trabalho consistirá no desenvolvimento do jogo de tabuleiro Quarto! recorrendo ao Prolog como linguagem de implementação. O projecto terá ainda um módulo de visualização gráfica 3D a ser implementado em linguagem C++. A aplicação final deverá permitir dois humanos jogarem entre si, assim como um jogo entre um humano e o computador, podendo o jogador escolher o nível de dificuldade que achar mais adequado, escolhendo, para tal, quais as regras a usar. Nota: Para mais informações quanto a este método de regulação do nível de dificuldade por favor consulte o capítulo: Descrição do Problema Introdução Pretende-se que com este trabalho se ganhe sensibilidade para o paradigma da programação em lógica, usando para tal, estratégias de resolução de problemas até agora pouco exploradas. Toma-se também como objectivo a aquisição de conhecimento no domínio da teoria dos jogos. A concretização destas motivações gerais na escolha do jogo em questão traduz-se no facto de este ser já do conhecimento de um dos elementos do grupo e de se ter a convicção que, muito embora o jogo se revista de uma aparente simplicidade, devido em parte ao reduzido número de regras, é na realidade um jogo muito interessante e desafiador das capacidades dos jogadores, tornado-se especialmente complexo quando se põem em campo todas as combinações de término permitidas. Outro aspecto que cativou a atenção dos elementos do grupo foram as diversas particularidades deste jogo que tornam a sua implementação muito diferente da dos jogos mais tradicionais. São de notar as seguintes características: As peças em jogo são comuns aos dois jogadores. É o jogador adversário que escolhe a peça a ser jogada. A avaliação do tabuleiro é particularmente difícil, na medida em que as peças são comuns e existe sempre uma dependência das peças ainda por jogar. É possível regular o grau de dificuldade do jogo, bastando escolher quais as regras a usar, podendo assim ser jogado por todo o tipo de jogadores. Será difícil encontrar maior motivação do que implementar o jogo mais premiado de todos os tempos! Página 2 de 13
Descrição, Representação & Visualização Descrição do Problema O jogo Quarto! foi criado por Blaise Muller, sendo comercializado em 1991 pela Gigamic S.A.. Pode-se dizer que o seu inventor foi beber inspiração a jogos mais tradicionais como o Jogo do Galo ou o Quatro em Linha, tendo-lhes adicionado um pequeno grupo de regras que fizeram deste jogo, num espaço de tempo pouco maior de que uma década, o jogo mais premiado de sempre [3,4]. Regras do Jogo Apresentação e Preparação Um tabuleiro de 16 casas. 16 peças diferentes tendo cada uma quatro características: cor clara ou escura, forma redonda ou quadrada, alta ou baixa, maciça ou oca. No começo da partida, as peças são dispostas ao lado do tabuleiro. Objectivo do Jogo Formar no tabuleiro um alinhamento de 4 peças que tenham no mínimo uma característica em comum. Esse alinhamento poderá ser horizontal, vertical ou diagonal. Desenvolvimento de uma Partida O primeiro jogador é tirado à sorte. De seguida escolhe uma das 16 peças e entrega-a ao seu adversário. Este deverá colocá-la numa das casas do tabuleiro e, em seguida, escolher uma das 15 peças restantes para entregar ao seu adversário. Por sua vez, este coloca a peça numa das casas disponíveis do tabuleiro e assim sucessivamente... Vencedor da Partida A partida é ganha pelo primeiro jogador que disser Quarto!. 1. Um jogador faz Quarto! e ganha a partida quando coloca a peça que lhe é dada: Forma um alinhamento de 4 peças de cor clara ou escura, ou 4 peças redondas ou quadradas, ou 4 peças altas ou baixas, ou 4 peças maciças ou ocas. Não é obrigatório que esse mesmo jogador tenha colocado as outras 3 peças. Ele deverá proclamar a sua vitória dizendo Quarto!. Página 3 de 13
Descrição, Representação & Visualização 2. Se esse jogador não vir o alinhamento e entregar uma peça ao adversário, este último pode nesse momento dizer Quarto!, e, mostrando tal alinhamento, ganhar a partida. 3. Se nenhum dos jogadores vir o alinhamento durante a vez em que jogam e quando ele se forma, ninguém poderá ganhar a partida e o jogo continuará. Fim da Partida Vitória: Um jogador diz e indica um Quarto! Igualdade: Todas as peças foram colocadas sem haver um vencedor. Tempo de Duração de uma Partida De 10 a 20 minutos, em média Em torneio, é possível dar a cada jogador um tempo limite de um minuto por jogada. Variante para Jogadores Iniciados (crianças...) Para se iniciar progressivamente pode-se jogar unicamente com 1, 2 ou 3 características como critérios de alinhamento. Exemplo: Formar no tabuleiro um alinhamento de 4 peças com a mesma cor (uma única característica). Variante para Jogadores Experimentados O objectivo do jogo é o de formar um alinhamento ou um quadrado de 4 peças que tenham no mínimo uma característica em comum. Existem assim 9 possibilidades suplementares de fazer Quarto!. Adicionalmente, pode ainda considerar-se como alinhamento quatro peças em L com uma característica em comum, no mínimo. Representação do Estado do Jogo Tratando-se de um jogo que se desenrola num tabuleiro com 4 linhas e outras tantas colunas, estando as casas organizadas de uma forma perfeitamente ortogonal e sendo as listas a estrutura de dados por excelência em Prolog, a forma escolhida para a representação do estado do tabuleiro foi a de uma lista de listas, em que cada um dos elementos representa uma peça ou um espaço disponível. Deste modo é fácil identificar a posição de cada uma das peças, usando para tal efeito, um sistema referencial bidimensional cartesiano. Tabuleiro vazio: tabuleiro( [ [22,22,22,22], [22,22,22,22], [22,22,22,22], [22,22,22,22] ] ). Na medida em que as peças são comuns aos 2 jogadores, torna-se também necessária manter a informação referente às peças ainda disponíveis. Para tal também foi usada uma lista de listas em tudo idêntica à usada para representar o tabuleiro de jogo. Página 4 de 13
Descrição, Representação & Visualização Peças disponíveis no inicio: reserva( [ [15,14,13,12], [11,10,9,8], [7,6,5,4], [3,2,1,0] ] ). Tendo o jogo 16 peças, todas elas diferentes mas tendo cada uma a possibilidade de assumir 2 estados diferentes e complementares de cada uma das 4 características, optou- -se por uma representação numérica das peças. Tal representação conterá em si mesma toda a informação necessária para caracterizar cada peça. Explorando a referida dualidade que cada uma das características pode assumir, atribuiu-se a cada peça um número de 0 a 15 em que cada digito na base 2 identifica univocamente cada característica da peça. A título de exemplo, a peça identificada pelo número 12, 1100 na base 2, é uma peça grande, de forma quadrada, de cor clara e oca, por outro lado a peça com o número 3, 0011 em binário será uma peça pequena, redonda, de cor escura e maciça. Foi reservado o número 22 para assinalar uma casa vazia. Representação de uma Jogada Atendendo, mais uma vez, à particularidade do jogo, também a definição de jogada não é a mais usual, na medida em que as jogadas se processam de uma forma peculiar e em duas fases. A primeira, em que um jogador coloca a peça previamente escolhida pelo seu adversário numa casa vazia no tabuleiro, e uma segunda em que escolhe uma qualquer peça disponível para entregar ao adversário. É de salientar que, quer a primeira jogada, quer a última, são meias-jogadas, na medida em que o jogador só tem de escolher a peça para entregar ao adversário, ou somente colocar a peça em jogo. Uma jogada pode assim ser definida como um tuplo variável consoante a altura de jogo: Mov = J-(X,Y)-P1-P2 % Jogador J coloca peça P1 em (X,Y) e escolhe peça P2 Mov = J-(X,Y)-P % Jogada final - Jogador J coloca peça P em (X,Y) Mov = J-P % Jogada inicial - Jogador J escolhe peça P para o adversário A Figura 1representa esquematicamente uma jogada. Ilustração Figura 1 1 - Diagrama - de de uma Jogada a Assim, quer para colocar uma peça no tabuleiro como para especificar uma peça para dar ao adversário usou-se a seguinte relação: Página 5 de 13
Descrição, Representação & Visualização insert_peca(peca,pecanova,x,y,tabuleiro,tabuleironovo):- insert_peca_aux(1,peca,pecanova,x,y,tabuleiro,tabuleironovo),!. O algoritmo usado é o de copiar linha a linha o conteúdo de Tabuleiro para TabuleiroNovo até chegar à linha onde se pretende inserir a peça; uma vez aí, passa-se a copiar elemento a elemento até se chegar à peça a substituir. Depois de trocar, copia-se elemento a elemento até acabar a linha em questão e, depois, novamente, linhas inteiras. Visualização do Tabuleiro Devido, novamente, às particularidades do jogo, e especialmente ao facto de todas as 16 peças serem diferentes, a sua representação em modo texto foi alvo de especial atenção. Ao contrário da maioria dos jogos tradicionais, em que uma peça poderia ser identificada por um único carácter, teve de se optar por uma representação numa matriz de caracteres de dimensão 16x8. Teve-se, ainda, de mostrar quais as peças ainda disponíveis para escolha, sendo no entanto tal representação em tudo semelhante à do tabuleiro de jogo. Assim, a relação que permite visualizar o tabuleiro mostra a linha que é a cabeça da lista, até ao fim da recursividade: mostra([],[],_). mostra( [L Resto], [H T], N):- mostra_tabuleiro([l Resto], [H T]):- draw(middle, N), N2 is N+1, mostra_linha_linha(l,h,1), draw(middle, N), draw(base), mostra(resto, T, N2). draw(top), mostra([l Resto], [H T], 1), nl. Por sua vez, por cada linha é mostrado linha a linha cada elemento até ao fim da lista: mostra_linha_linha(l, T, 8). mostra_linha_linha(l, T, N):- mostra_linha(l,n),write(' '), mostra_linha(t,n),write(' '), N1 is N+1, mostra_linha_linha(l, T, N1). Como cada elemento é desenhado linha a linha, também tem de ser desenhado recursivamente: mostra_linha([],_). mostra_linha( [Elem Resto], N):- draw(elem, N),mostra_linha(Resto, N). A Figura 2 representa esquematicamente o funcionamento destes predicados: Figura 2 - Diagrama de Desenho do Tabuleiro de Jogo Página 6 de 13
Conclusões & Bibliografia Conclusões e Perspectivas de Desenvolvimento Analisando o trabalho desenvolvido, chega-se à conclusão que foram atingidos todos os objectivos propostos, entendendo como satisfatórios os recursos à nossa disposição, documentos e equipamentos. Sempre realçando que se apresenta uma primeira versão extremamente primária quando comparada com o produto final, conclui-se que os módulos já desenvolvidos, sendo de destacar a representação do tabuleiro e a visualização do mesmo e das peças, aparentam já ter a forma que terão na versão final. Antecipando objectivos, é já possível a realização de um jogo entre dois humanos. No entanto, este último módulo, muito embora esteja já operacional e funcional, carece de validação de jogadas e de adicional informação para os jogadores. Feito o balanço entre os objectivos alcançados, tenham sido exigidos ou antecipados, estima-se que já se tenha atingido o marco dos 35% de trabalho cumprido. Bibliografia Para a realização do trabalho: [1] Eugénio Oliveira e Luís Paulo Reis, Materiais da Disciplina de Programação em Lógica, disponível online a partir de http://www.fe.up.pt/~eol/lp/0304 (Consultado em Outubro de 2003). [2] Vários Autores, SICStus Prolog User s Manual, Release 3.10.1, Abril de 2003. Para a realização do relatório: [3] Vários Autores, Educational Learning Games - Quarto, disponível online em http://www.educationallearninggames.com/quarto-game.asp (Consultado em Outubro de 2003). [4] Vários Autores, Manual do jogo Quarto!. Página 7 de 13
Anexo A Exemplo de Modo de Utilização Representação do tabuleiro de jogo inicialmente vazio e todas as peças disponíveis para serem escolhidas. Indicação da posição da peça escolhida, neste caso a coordenada (1,1). Mudança de jogador, retirada da peça escolhida pelo adversário da tabela de peças disponíveis. i
Anexo A Exemplo de Modo de Utilização Indicação da posição no tabuleiro para colocação da peça escolhida pelo adversário. Colocação efectiva da peça no tabuleiro de jogo e espera por indicação da posição da peça para entregar ao adversário. Todas as outras jogadas processam-se de forma idêntica. Apresenta-se, de seguida, uma possível jogada final. Especificação da posição no tabuleiro para a colocação da peça assinalada. ii
Anexo A Exemplo de Modo de Utilização Colocação da peça no tabuleiro. Assinalamento do alinhamento das 4 peças com característica em comum. Neste caso é de notar que se está a considerar a modalidade mais complexa, em que um alinhamento em L é também considerado válido. A característica em comum seria o facto de as peças serem ocas. iii
Anexo B Código draw(12,3):- write(' '). draw(14,3):- write(' - - - '). draw(9,3):- write(' / \\ '). draw(8,3):- write(' / \\ '). draw(11,3):- write(' /- - - - \\ '). draw(11,4):- write(' - - - - - '). draw(11,5):- write(' - - - - - '). draw(10,3):- write(' /- - - \\ '). draw(4,3):- write(' '). draw(6,3):- write(' - - '). draw(3,3):- write(' /- - \\ '). draw(3,4):- write(' - - - '). draw(2,3):- write(' /- -\\ '). draw(0,3):- write(' / \\ '). draw(1,3):- write(' / \\ '). draw(middle,n):- write(' '), write(n), write(' '). draw(x,1):- 3=:=X>>2, write(' '). %todos os grandes quadrados draw(x,7):- 3=:=X>>2, write(' ').%todos os grandes quadrados draw(x,1):- 2=:=X>>2, write(' '). %todos os grandes redondos draw(x,7):- 2=:=X>>2, write(' \\ / '). %todos os grandes redondos draw(x,2):- 4=:=X>>1, write(' / \\ '). %todos os grandes redondos claros draw(x,6):- 4=:=X>>1, write(' \\ / '). %todos os grandes redondos claros draw(x,2):- 5=:=X>>1, write(' /- - - \\ '). %todos os grandes redondos escuros draw(x,6):- 5=:=X>>1, write(' \\ - - - -/ '). %todos os grandes redondos escuros draw(x,n):- (X==15,0=:=N mod 2;X==14,(N==2;N==6)), write(' - - - - - ').%todos os grandes quadrados escuros draw(13,n):- N>1, N<7, write(' '). % dos grandes quadrados claros maciços draw(15,n):- (N==3;N==5), write(' - - - - - ').% dos grandes quadrados escuros maciços draw(12,n):- (N==2;N==6), write(' ').% dos grandes quadrados claros ocos draw(9,n):- (N==4;N==5), write(' '). % dos grandes redondos claros maciços draw(x,4):- (X==8;X==12), write(' '). %todos os grandes e claros e ocos draw(x,5):- (X==8;X==12), write(' '). %todos os grandes e claros e ocos draw(x,4):- (X==10;X==14), write(' - - - '). %todos os grandes e escuros e ocos draw(x,5):- (X==10;X==14), write(' - - - '). %todos os grandes e escuros e ocos draw(x,n):- (N==1;N==7),0=:=X>>3, write(' ').%todos os pequenos draw(x,2):- 1=:=X>>2, write(' '). %todos os pequenos e quadrados draw(x,6):- 1=:=X>>2, write(' '). %todos os pequenos e quadrados draw(x,6):- 0=:=X>>2, write(' \\ / '). %todos os pequenos e redondos draw(x,2):- 0=:=X>>2, write(' '). %todos os pequenos e redondos draw(x,n):- (N==4,X==7;N==5,X==3), write(' - - - ').%todos os pequenos e escuros e maciços draw(x,4):- (X==2;X==6), write(' - - '). %todos os pequenos e escuros e ocos draw(x,5):- (X==2;X==6), write(' - - '). %todos os pequenos e escuros e ocos draw(x,4):- (X==0;X==4), write(' '). %todos os pequenos e claros e ocos draw(x,5):- (X==0;X==4), write(' '). %todos os pequenos e claros e ocos draw(1,n):- (N==4;N==5), write(' '). % dos pequenos redondos claros maciços draw(7,n):- (N==3;N==5), write(' - - - '). % dos pequenos quadrados escuros maciços draw(5,n):- (N==3;N==4;N==5), write(' ').% dos pequenos quadrados claros maciços draw(top) :- write(' 1 2 3 4 1 2 3 4 ' ), i
Anexo B Código write(' ' ). draw(base) :- write(' '). draw(22, N) :- write(' '). tabuleiro( [ [22,22,22,22], [22,22,22,22], [22,22,22,22], [22,22,22,22] ] ). reserva( [ [15,14,13,12], [11,10,9,8], [7,6,5,4], [3,2,1,0] ] ). mostra([],[],_). mostra( [L Resto], [H T], N):- draw(middle, N), N2 is N+1, mostra_linha_linha(l,h,1), draw(middle, N), draw(base), mostra(resto, T, N2). mostra_tabuleiro([l Resto], [H T]):- draw(top), mostra([l Resto], [H T], 1), nl. mostra_linha([],_). mostra_linha( [Elem Resto], N):- draw(elem, N), mostra_linha(resto, N). mostra_linha_linha(l, T, 8). mostra_linha_linha(l, T, N):- mostra_linha(l,n),write(' '), mostra_linha(t,n),write(' '), N1 is N+1, mostra_linha_linha(l, T, N1). % Copyright LPR 1999 insert_peca(peca,pecanova,x,y,tabuleiro,tabuleironovo):- insert_peca_aux(1,peca,pecanova,x,y,tabuleiro,tabuleironovo),!. insert_peca_aux(_,_,_,_,_,[],[]). insert_peca_aux(y,peca,pecanova,x,y,[lin Resto],[NovLin Resto2]):- %linha com a peça a substituir insert_peca_linha(1,peca,pecanova,x,lin,novlin), N2 is Y+1, insert_peca_aux(n2,peca,pecanova,x,y,resto,resto2). insert_peca_aux(n,peca,pecanova,x,y,[lin Resto],[Lin Resto2]):- %linha a copiar por inteiro N\=Y, N2 is N+1, insert_peca_aux(n2,peca,pecanova,x,y,resto,resto2). insert_peca_linha(_,_,_,_,[],[]). insert_peca_linha(x,peca,pecanova,x,[peca Resto],[PecaNova Resto2]):- %posição da peça N2 is X+1, insert_peca_linha(n2,peca,pecanova,x,resto,resto2). insert_peca_linha(n,peca,pecanova,x,[el Resto],[El Resto2]):- N\=X, N2 is N+1, insert_peca_linha(n2,peca,pecanova,x,resto,resto2). % End of Copyright LPR 1999 jogo :- tabuleiro(x), reserva(y), nl,nl, joga(x,y,1). ii
Anexo B Código joga(tabuleiro, Reserva, Jogador):- nl,nl,nl,nl,nl,nl,nl,nl, Xorigem, Yorigem), NovoTabuleiro), mostra_tabuleiro(tabuleiro,reserva), read(xorigem), read(yorigem),nl, busca_peca(peca, insert_peca(peca, 22, Xorigem, Yorigem, Reserva, Z), mostra_tabuleiro(tabuleiro,z), nl, mostra_peca(peca, 1), nl, muda_jogador(jogador, NovoJogador), read(xdes), read(ydes),nl,nl, insert_peca(22, Peca, Xdes, Ydes, Tabuleiro, joga(novotabuleiro, Z, NovoJogador),nl,nl. muda_jogador(1,2). muda_jogador(2,1). mostra_peca(peca, 8):- write(' '). mostra_peca(peca, N):-draw(Peca,N), nl, N2 is N+1, mostra_peca(peca,n2). busca_peca(peca, X, Y):- Peca is (((4-Y)<<2) + (4-X)). iii