Pular para o conteúdo principal

victorstein.dev

60/100 Dias de Golang - Jogo em UDP - Parte 6

Table of Contents

# Jogo em UDP - Parte 6

Para fechar o jogo, vamos criar um objetivo. Qual player chegar primeiro na coordenada X, Y vence. O que vamos ter que alterar: fazer uma verificação se algum player está na posição de vitória, enviar uma mensagem de vitória depois que alguém alcançar a posição e travar a movimentação dos players após a vitória.

Vamos primeiro adicionar nossas variáveis:

var goalPosition = [2]int{arenaRows - 2, arenaCols - 2}
var gameOver = false
var winner string

Vamos adicionar uma verificação, caso gameOver seja verdade, não enviamos mais os comando para o canal

if !gameOver {
	commands <- Command{PlayerID: playerID, Action: msg}
}

E a verificação ficará de responsabilidade da goroutine que trata as informações recebidas do usuário:

if player.Y == goalPosition[0] && player.X == goalPosition[1] {
	gameOver = true
	winner = string(player.Char)
	fmt.Printf("Jogador %s venceu!\n", winner)
}

E para fechar, colocamos um “X” no grid e adicionamos uma mensagem do jogador vencedor:

grid[goalPosition[0]][goalPosition[1]] = 'X'

if gameOver {
	sb.WriteString(fmt.Sprintf("=== JOGO ENCERRADO! VENCEDOR: %s ===\n", winner))
} else {
	sb.WriteString("=== BATALHA EM ANDAMENTO ===\n")
}

Veja como fica o código completo:

package main

import (
	"fmt"
	"net"
	"strings"
	"sync"
	"time"
)

const arenaRows = 20
const arenaCols = 60

var grid = make([][]rune, arenaRows)
var goalPosition = [2]int{arenaRows - 2, arenaCols - 2}
var gameOver = false
var winner string

type Player struct {
	ID   string
	Char rune
	X, Y int
	Addr *net.UDPAddr
}

var players = make(map[string]*Player)
var playersMutex sync.Mutex

var commands = make(chan Command, 100)

type Command struct {
	PlayerID string
	Action   string
}

func main() {
	addr, _ := net.ResolveUDPAddr("udp", "0.0.0.0:8080")
	conn, _ := net.ListenUDP("udp", addr)
	defer conn.Close()

	fmt.Println("Servidor iniciado na porta 8080...")

	go func() {
		buf := make([]byte, 1024)
		for {
			n, clientAddr, _ := conn.ReadFromUDP(buf)
			msg := strings.TrimSpace(string(buf[:n]))
			playerID := clientAddr.String()

			playersMutex.Lock()
			if _, exists := players[playerID]; !exists {
				players[playerID] = &Player{
					ID:   playerID,
					Char: rune('A' + len(players)%26),
					X:    1,
					Y:    1,
					Addr: clientAddr,
				}
				fmt.Printf("Novo jogador %s (%c)\n", playerID, players[playerID].Char)
			}
			playersMutex.Unlock()

			if !gameOver {
				commands <- Command{PlayerID: playerID, Action: msg}
			}
		}
	}()

	go func() {
		for cmd := range commands {
			if gameOver {
				continue
			}

			playersMutex.Lock()
			player := players[cmd.PlayerID]
			switch cmd.Action {
			case "w":
				if player.Y > 0 && grid[player.Y-1][player.X] != '*' {
					player.Y--
				}
			case "s":
				if player.Y < arenaRows-1 && grid[player.Y+1][player.X] != '*' {
					player.Y++
				}
			case "a":
				if player.X > 0 && grid[player.Y][player.X-1] != '*' {
					player.X--
				}
			case "d":
				if player.X < arenaCols-1 && grid[player.Y][player.X+1] != '*' {
					player.X++
				}
			}

			if player.Y == goalPosition[0] && player.X == goalPosition[1] {
				gameOver = true
				winner = string(player.Char)
				fmt.Printf("Jogador %s venceu!\n", winner)
			}

			playersMutex.Unlock()
		}
	}()

	ticker := time.NewTicker(200 * time.Millisecond)
	for range ticker.C {
		arena := renderArena()
		sendArena(conn, arena)
	}
}

func renderArena() string {
	for i := 0; i < arenaRows; i++ {
		grid[i] = make([]rune, arenaCols)
		for j := range grid[i] {
			if i == 0 || i == arenaRows-1 || j == 0 || j == arenaCols-1 {
				grid[i][j] = '*'
			} else {
				grid[i][j] = '.'
			}
		}
	}

	grid[goalPosition[0]][goalPosition[1]] = 'X'

	playersMutex.Lock()
	for _, p := range players {
		grid[p.Y][p.X] = p.Char
	}
	playersMutex.Unlock()

	var sb strings.Builder
	if gameOver {
		sb.WriteString(fmt.Sprintf("=== JOGO ENCERRADO! VENCEDOR: %s ===\n", winner))
	} else {
		sb.WriteString("=== BATALHA EM ANDAMENTO ===\n")
	}
	for _, row := range grid {
		sb.WriteString(string(row))
		sb.WriteRune('\n')
	}

	return sb.String()
}

func sendArena(conn *net.UDPConn, arena string) {
	playersMutex.Lock()
	for _, p := range players {
		conn.WriteToUDP([]byte(arena), p.Addr)
	}
	playersMutex.Unlock()
}