Olá amigos e futuros colegas! Recentemente fiz um teste para participar de um projeto real, passei, mas aconteceu que por motivos pessoais não pude participar do próprio RP. Depois de problemas tão interessantes como o teste de RP, os problemas habituais do curso tornaram-se um passatempo menos atraente, especialmente porque eu já tinha resolvido a maioria deles. Portanto, para que meu talento não fosse desperdiçado para continuar aprendendo, decidi criar um web game multiplayer. Links para outros jogos:
Tic Tac Toe parecia o jogo mais simples para mim, então decidi dividir a tarefa em uma série de subtarefas:
O primeiro algoritmo está verificando todo o campo
- Aplicativo de console para testar a lógica do jogo
- Multijogador
- Anexando um banco de dados de jogadores a um aplicativo de console
- Criação de design front-end, escrita de modelos de página, interface de jogo
- Juntando tudo
- campo
- dois jogadores que se revezam, um faz uma cruz e o segundo um zero. é simples.
String[][] strings = {{"O", "O", "_"},
{"_", "X", "O"},
{"X", "X", "X"},
for (String [] ss : strings){
for (String s : ss) System.out.print(s + " ");
System.out.println(); //для перевода строки
}
a tela exibiria:
O O _
_ X O
X X X
Mas além do display, temos também uma comparação de valores, e aqui as opções já são possíveis. Você pode comparar strings, pode criar uma classe de enumeração especial ( enum
), mas eu preferiria comparar números e substituí-los por “X” e “O” somente quando exibidos na tela. Seja, por exemplo, 1 - "X", 2 - "O", 0 - "_". Então, como você verifica se há uma correspondência tripla X ou O em um campo?
O primeiro algoritmo está verificando todo o campo
int[][] canvas = {{00, 01, 02},
{10, 11, 12},
{20, 21, 22}}
Combinações vencedoras:
00-01-02, 10-11-12, 20-21-22, 00-10-20, 01-11-21, 02-12-22, 00-11-22, 20-11-02 — всего 8.
Verificando comparando números, mas acontece que você precisa verificar o campo INTEIRO, todas as 8 combinações, todas as vezes. Claro, isso não é muito, não se trata de uma busca por números de Armstrong na faixa de 0 a 1 bilhão, há pouco mais do que nenhum cálculo aqui, mas você ainda quer algo mais ideal do que verificar todo o campo. A segunda ideia que me veio foi verificar apenas a célula que estava marcada na jogada anterior, assim ainda poderemos determinar o vencedor, pois saberemos quem fez essa jogada. Assim, em vez de todas as 8 combinações, obtemos apenas 2, 3 ou 4 combinações, dependendo da célula, veja a imagem: Agora precisamos descobrir como determinar qual combinação precisa ser iniciada? Foi aqui que percebi que usar um array bidimensional não é muito conveniente. Decidi considerar outras opções. A princípio tive a ideia de que o campo pode ser mantido em um número de nove dígitos, por exemplo, o mesmo campo que exibimos na tela pode ser escrito assim: 220012111, vou explicar nos dedos o que é é... O código é o mesmo, 1 - “X”, 2 - “O” , 0 – " ", o que significa 220012111 = "OO__XOXXX", ou se após cada terceiro dígito você inserir uma quebra de linha e adicionar espaços para clareza:
О О _
_ Х О
Х Х Х
aqui novamente, conveniente para armazenamento, foi inventado um dispositivo para exibição, mas inconveniente para comparação! A solução foi encontrada quando numerei as células de 1 a 9, então pensei, porque na programação a contagem regressiva começa em 0 e numerei como na foto. Você não notou nenhuma peculiaridade? se você olhar a imagem acima, ficará claro que soluções com 2 combinações têm um número de série ímpar, 4 combinações têm o número de série 4, 3 combinações têm o resto. Então cheguei à conclusão de que é necessário manter o campo de jogo em uma matriz regular de números: simples iteração entre números, capacidade de comparação de acordo com o algoritmo escolhido, simples saída para a tela. Quanto ao próprio algoritmo de comparação. Vamos pela ordem: em todas as opções há uma verificação de linha e coluna, verificamos apenas elas. se a pesquisa não der resultado, verificamos o número da célula para par/ímpar, se for ímpar, então voltamos ao jogo, não adianta verificar mais, se for par, verificamos se esta célula está no diagonal esquerda, os números desta diagonal quando divididos por 4 têm resto 0. Se mentir, verificamos se há correspondências, se nenhuma correspondência for encontrada, então verificamos o número 4, se não, voltamos ao jogo, se sim, avançamos no código e retornamos o resultado da verificação da última diagonal. Provavelmente para uma pessoa despreparada isso é difícil de entender depois de ler o conjunto de letras acima, mas alguém pode dizer que tem muitas letras no próprio código, que pode ser mais simples, terei prazer em discutir isso. Já resolvemos o campo, agora precisamos lidar com dois usuários que se revezam e cada um deles tem seu sinal, X ou O. Na primeira etapa não temos nenhuma funcionalidade multiusuário, então o a maneira mais fácil seria usar os ícones um por um. X sempre faz o primeiro movimento, O sempre faz o segundo, depois X novamente e assim por diante. Ele implora para ser verificado ( true/false ), e se for verdadeiro – então o jogador atual é X, se for falso – então O e no início de cada movimento flag=!flag Resta receber de alguma forma um sinal dos jogadores sobre qual célula que eles escolherem. Aqui precisaremos de nossos inesquecíveis. BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Os jogadores inserirão os números dos celulares no console e pressionar Enter fará um movimento. A célula correspondente ao número inserido mudará seu valor de 0 para 1 ou 2, dependendo do estado atual da caixa de seleção, discutido no parágrafo acima. É aqui que é importante validar a entrada para que ninguém possa alterar X para O quando a célula já estiver preenchida :) O que o jogador pode inserir no console?
- linha vazia
- letras, sinais de pontuação, colchetes... em uma palavra, não números
- números incorretos - negativos ou fora do tamanho do array, células ocupadas.
Integer.parseInt("2");
Ele lança uma exceção NumberFormatException
se não conseguir obter um dígito de uma determinada string. Podemos fornecer proteção dos dois primeiros pontos interceptando essa exceção. Para o terceiro ponto, eu criaria outro método que verifica o valor inserido, mas seria mais correto mover a solicitação de string para um método separado no qual a validação será realizada e retornará apenas um número. Para resumir, criamos um campo, fizemos um método que o exibe, fizemos um método que verifica “este jogador ganhou por uma hora?”, e validamos os números inseridos. Resta muito pouco a fazer, verificar se há empate - um método separado que percorre o array e procura 0 e exibe os resultados do jogo. Só isso, o código está pronto, o jogo acabou ficando pequeno, só uma classe, então copiadores durões podem, sem entender, apenas copiar tudo no projeto deles e rodar sozinho, eu mesmo era assim, mas agora procuro não fazer isso e não recomendo a ninguém :) Boa sorte a todos no aprendizado de JAVA! ps o resto dos pontos - multiplayer e banco de dados virão mais tarde, já comecei a estudar o material :)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class GameField {
static int [] canvas = {0,0,0,
0,0,0,
0,0,0};
//012, 345, 678, 036, 147, 258, 048, 246
public static void main(String[] args){
boolean b;
boolean isCurrentX = false;
do {
isCurrentX = !isCurrentX;
drawCanvas();
System.out.println("mark " + (isCurrentX ? "X" : "O"));
int n = getNumber();
canvas[n] = isCurrentX ? 1 : 2;
b = !isGameOver(n);
if (isDraw()){
System.out.println("Draw");
return;
}
} while (b);
drawCanvas();
System.out.println();
System.out.println("The winner is " + (isCurrentX ? "X" : "O") + "!");
}
static int getNumber(){
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true){
try {
int n = Integer.parseInt(reader.readLine());
if (n >= 0 && n < canvas.length && canvas[n]==0){
return n;
}
System.out.println("Choose free cell and enter its number");
} catch (NumberFormatException e) {
System.out.println("Please enter the number");
} catch (IOException e) {
}
}
}
static boolean isGameOver(int n){
// 0 1 2
// 3 4 5
// 6 7 8
//поиск совпадений по горизонтали
int row = n-n%3; //номер строки - проверяем только её
if (canvas[row]==canvas[row+1] &&
canvas[row]==canvas[row+2]) return true;
//поиск совпадений по вертикали
int column = n%3; //номер столбца - проверяем только его
if (canvas[column]==canvas[column+3])
if (canvas[column]==canvas[column+6]) return true;
//мы здесь, значит, первый поиск не положительного результата
//если meaning n находится на одной из граней - возвращаем false
if (n%2!=0) return false;
//проверяем принадлежит ли к левой диагонали meaning
if (n%4==0){
//проверяем есть ли совпадения на левой диагонали
if (canvas[0] == canvas[4] &&
canvas[0] == canvas[8]) return true;
if (n!=4) return false;
}
return canvas[2] == canvas[4] &&
canvas[2] == canvas[6];
}
static void drawCanvas(){
System.out.println(" | | ");
for (int i = 0; i < canvas.length; i++) {
if (i!=0){
if (i%3==0) {
System.out.println();
System.out.println("_____|_____|_____");
System.out.println(" | | ");
}
else
System.out.print("|");
}
if (canvas[i]==0) System.out.print(" " + i + " ");
if (canvas[i]==1) System.out.print(" X ");
if (canvas[i]==2) System.out.print(" O ");
}
System.out.println();
System.out.println(" | | ");
}
public static boolean isDraw() {
for (int n : canvas) if (n==0) return false;
return true;
}
}
GO TO FULL VERSION