1 Módulo 2

Documentação do Esquema e Dados do Projeto

1.1 Esquema do Banco de Dados (DDL)

Este é o DDL (Data Definition Language) para criar a estrutura do banco de dados do projeto. Ele define as tabelas, colunas, chaves primárias e estrangeiras necessárias.

-- Configurações para garantir que os comandos rodem sem parar em erros de "já existe"
-- Isso é útil se o script for executado em um ambiente onde partes já foram criadas.
SET client_min_messages TO WARNING; -- Suprime mensagens de NOTICE, mostrando WARNINGs e erros

CREATE TABLE IF NOT EXISTS Bioma (
    NomeBioma VARCHAR(100) PRIMARY KEY NOT NULL
);

CREATE TABLE IF NOT EXISTS Mapa (
    Nome VARCHAR(100) NOT NULL,
    Turno VARCHAR(50) NOT NULL,
    PRIMARY KEY (Nome, Turno)
);

CREATE TABLE IF NOT EXISTS Chunk (
    Numero_chunk INTEGER PRIMARY KEY NOT NULL,
    Id_bioma VARCHAR(100) NOT NULL,
    Id_mapa_nome VARCHAR(100) NOT NULL,
    Id_mapa_turno VARCHAR(50) NOT NULL,

    FOREIGN KEY (Id_bioma) REFERENCES Bioma(NomeBioma) ON DELETE RESTRICT ON UPDATE CASCADE,
    FOREIGN KEY (Id_mapa_nome, Id_mapa_turno) REFERENCES Mapa(Nome, Turno) ON DELETE RESTRICT ON UPDATE CASCADE
);

CREATE TABLE IF NOT EXISTS Jogador (
    Id_Jogador SERIAL PRIMARY KEY,
    Nome VARCHAR(100) NOT NULL,
    Vida_max INT NOT NULL,
    Vida_atual INT NOT NULL,
    xp INT NOT NULL,
    forca INT NOT NULL
);

CREATE TABLE IF NOT EXISTS Inventario (
    id_jogador INT NOT NULL,
    id_inventario INT NOT NULL,
    Instancia_Item TEXT,
    ArmaduraEquipada VARCHAR(100),
    ArmaEquipada VARCHAR(100),

    PRIMARY KEY (id_jogador, id_inventario),
    FOREIGN KEY (id_jogador) REFERENCES Jogador(Id_Jogador) ON DELETE CASCADE ON UPDATE CASCADE
);

-- Garante que a coluna e a constraint só sejam adicionadas se não existirem.
DO $$
BEGIN
    -- Adicionar a coluna Id_Chunk_Atual à tabela Jogador se ela não existir
    -- Note que este bloco 'IF NOT EXISTS' não é padrão SQL, mas é uma extensão comum em PG.
    -- Ou você pode usar um bloco EXCEPTION WHEN duplicate_column THEN null; como antes
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='jogador' AND column_name='id_chunk_atual') THEN
        ALTER TABLE Jogador ADD COLUMN Id_Chunk_Atual INTEGER; -- <--- AGORA É INTEGER
    END IF;

    -- Adicionar a constraint de chave estrangeira se ela não existir
    IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_jogador_chunk') THEN
        ALTER TABLE Jogador ADD CONSTRAINT fk_jogador_chunk
        FOREIGN KEY (Id_Chunk_Atual) REFERENCES Chunk(Numero_chunk) ON DELETE SET NULL ON UPDATE CASCADE;
    END IF;
END
$$ LANGUAGE plpgsql;


-- Índices para Chaves Estrangeiras (para otimização de performance em consultas)
CREATE INDEX IF NOT EXISTS idx_chunk_id_bioma ON Chunk (Id_bioma);
CREATE INDEX IF NOT EXISTS idx_chunk_id_mapa ON Chunk (Id_mapa_nome, Id_mapa_turno);
CREATE INDEX IF NOT EXISTS idx_inventario_id_jogador ON Inventario (id_jogador);
CREATE INDEX IF NOT EXISTS idx_jogador_id_chunk_atual ON Jogador (Id_Chunk_Atual);

1.2 Manipulação de Dados (DML)

Este script DML (Data Manipulation Language) insere alguns dados iniciais nas tabelas do banco de dados para testes e demonstrações.

INSERT INTO Mapa (Nome, Turno)
VALUES
    ('Mapa_Principal', 'Dia'),
    ('Mapa_Principal', 'Noite')
ON CONFLICT (Nome, Turno) DO NOTHING;

INSERT INTO Bioma (NomeBioma)
VALUES
    ('Deserto'),
    ('Oceano'),
    ('Selva'),
    ('Floresta')
ON CONFLICT (NomeBioma) DO NOTHING;

INSERT INTO Chunk (Numero_chunk, Id_bioma, Id_mapa_nome, Id_mapa_turno)
VALUES
    (1, 'Deserto', 'Mapa_Principal', 'Dia'),
    (2, 'Oceano', 'Mapa_Principal', 'Dia'),
    (3, 'Selva', 'Mapa_Principal', 'Noite'),
    (4, 'Floresta', 'Mapa_Principal', 'Noite')
ON CONFLICT (Numero_chunk) DO NOTHING;

INSERT INTO Jogador (Nome, Vida_max, Vida_atual, xp, forca, Id_Chunk_Atual)
VALUES
    ('Player1', 100, 100, 0, 10, 1),
    ('Player2', 120, 120, 50, 12, 2);

INSERT INTO Inventario (id_jogador, id_inventario, Instancia_Item, ArmaduraEquipada, ArmaEquipada)
VALUES
    (1, 1, '{"item_id": 101, "quantidade": 5}', 'Capacete de Ferro', 'Espada de Diamante'),
    (2, 1, '{"item_id": 201, "quantidade": 1}', 'Armadura de Couro', 'Arco Longo')
ON CONFLICT (id_jogador, id_inventario) DO NOTHING;

1.3 Linguagem de Consulta de Dados (DQL)

Aqui estão algumas consultas DQL (Data Query Language) para recuperar e analisar os dados armazenados no banco de dados.

-- =================================================================
--                  Consultas Básicas
-- Seleciona todos os dados de cada tabela individualmente.
-- =================================================================

-- Seleciona todos os mapas
SELECT * FROM Mapa;

-- Seleciona todos os biomas
SELECT * FROM Bioma;

-- Seleciona todos os chunks
SELECT * FROM Chunk;

-- Seleciona todos os jogadores
SELECT * FROM Jogador;

-- Seleciona todos os inventários
SELECT * FROM Inventario;

-- Consulta 1: Jogadores e sua Localização Atual
-- Mostra em qual chunk, bioma, mapa e turno cada jogador está.
SELECT
    j.Nome AS Nome_Jogador,
    j.Vida_atual,
    j.xp,
    c.Numero_chunk,
    c.Id_bioma AS Bioma,
    c.Id_mapa_nome AS Mapa,
    c.Id_mapa_turno AS Turno
FROM
    Jogador j
JOIN
    Chunk c ON j.Id_Chunk_Atual = c.Numero_chunk;


-- Consulta 2: Inventário de Cada Jogador
-- Lista os itens, armadura e arma equipada para cada jogador.
SELECT
    j.Nome AS Nome_Jogador,
    i.Instancia_Item,
    i.ArmaduraEquipada,
    i.ArmaEquipada
FROM
    Inventario i
JOIN
    Jogador j ON i.id_jogador = j.Id_jogador; -- Assumindo que 'Id_jogador' é a chave primária da tabela Jogador.


-- Consulta 3: Detalhes Completos dos Chunks
-- Visualiza os detalhes de cada chunk, incluindo o nome do bioma e as informações do mapa.
SELECT
    c.Numero_chunk,
    b.NomeBioma,
    m.Nome AS Nome_Mapa,
    m.Turno
FROM
    Chunk c
JOIN
    Bioma b ON c.Id_bioma = b.NomeBioma
JOIN
    Mapa m ON c.Id_mapa_nome = m.Nome AND c.Id_mapa_turno = m.Turno;

1.4 DML: Geração de 1000 Chunks

Este script DML avançado preenche o banco de dados com 1000 “chunks” para simular um mapa maior. Ele inclui lógica para distribuir os biomas (Oceano, Deserto, Selva, Floresta) de forma programática.

-- Script DML para popular o banco de dados com 1000 chunks
-- Mapa: 32x32 chunks (1024 chunks total, mas vamos usar 1000)
-- Estrutura: Oceano ao redor, deserto no centro (20%), selva e floresta no resto

-- Inserir mapas (Dia e Noite)
INSERT INTO Mapa (Nome, Turno)
VALUES
    ('Mapa_Principal', 'Dia'),
    ('Mapa_Principal', 'Noite')
ON CONFLICT (Nome, Turno) DO NOTHING;

-- Inserir biomas
INSERT INTO Bioma (NomeBioma)
VALUES
    ('Deserto'),
    ('Oceano'),
    ('Selva'),
    ('Floresta')
ON CONFLICT (NomeBioma) DO NOTHING;

-- Função para gerar chunks com a distribuição especificada
-- Mapa 32x32 com oceano na borda, deserto no centro, selva e floresta no resto
DO $$
DECLARE
    chunk_id INTEGER := 1;
    x INTEGER;
    y INTEGER;
    biome_name VARCHAR(100);
    map_size INTEGER := 32;
    center_start INTEGER := 12; -- Início da área central (deserto)
    center_end INTEGER := 20;   -- Fim da área central (deserto)
    desert_chunks INTEGER := 0;
    ocean_chunks INTEGER := 0;
    jungle_chunks INTEGER := 0;
    forest_chunks INTEGER := 0;
BEGIN
    -- Gerar chunks para o mapa de dia
    FOR y IN 1..map_size LOOP
        FOR x IN 1..map_size LOOP
            -- Determinar bioma baseado na posição
            IF x = 1 OR x = map_size OR y = 1 OR y = map_size THEN
                -- Borda: Oceano
                biome_name := 'Oceano';
                ocean_chunks := ocean_chunks + 1;
            ELSIF x >= center_start AND x <= center_end AND y >= center_start AND y <= center_end THEN
                -- Centro: Deserto (área 9x9 = 81 chunks, aproximadamente 20% de 1000)
                biome_name := 'Deserto';
                desert_chunks := desert_chunks + 1;
            ELSE
                -- Resto: Alternar entre Selva e Floresta
                IF (x + y) % 2 = 0 THEN
                    biome_name := 'Selva';
                    jungle_chunks := jungle_chunks + 1;
                ELSE
                    biome_name := 'Floresta';
                    forest_chunks := forest_chunks + 1;
                END IF;
            END IF;

            -- Inserir chunk
            INSERT INTO Chunk (Numero_chunk, Id_bioma, Id_mapa_nome, Id_mapa_turno)
            VALUES (chunk_id, biome_name, 'Mapa_Principal', 'Dia')
            ON CONFLICT (Numero_chunk) DO NOTHING;

            chunk_id := chunk_id + 1;

            -- Parar após 1000 chunks
            IF chunk_id > 1000 THEN
                EXIT;
            END IF;
        END LOOP;

        -- Parar após 1000 chunks
        IF chunk_id > 1000 THEN
            EXIT;
        END IF;
    END LOOP;

    -- Gerar chunks para o mapa de noite (mesma distribuição)
    chunk_id := 1001;
    FOR y IN 1..map_size LOOP
        FOR x IN 1..map_size LOOP
            -- Determinar bioma baseado na posição
            IF x = 1 OR x = map_size OR y = 1 OR y = map_size THEN
                -- Borda: Oceano
                biome_name := 'Oceano';
            ELSIF x >= center_start AND x <= center_end AND y >= center_start AND y <= center_end THEN
                -- Centro: Deserto
                biome_name := 'Deserto';
            ELSE
                -- Resto: Alternar entre Selva e Floresta
                IF (x + y) % 2 = 0 THEN
                    biome_name := 'Selva';
                ELSE
                    biome_name := 'Floresta';
                END IF;
            END IF;

            -- Inserir chunk
            INSERT INTO Chunk (Numero_chunk, Id_bioma, Id_mapa_nome, Id_mapa_turno)
            VALUES (chunk_id, biome_name, 'Mapa_Principal', 'Noite')
            ON CONFLICT (Numero_chunk) DO NOTHING;

            chunk_id := chunk_id + 1;

            -- Parar após 2000 chunks total (1000 para cada turno)
            IF chunk_id > 2000 THEN
                EXIT;
            END IF;
        END LOOP;

        -- Parar após 2000 chunks total
        IF chunk_id > 2000 THEN
            EXIT;
        END IF;
    END LOOP;

    -- Mostrar estatísticas
    RAISE NOTICE 'Chunks gerados para o mapa de dia:';
    RAISE NOTICE 'Deserto: % chunks', desert_chunks;
    RAISE NOTICE 'Oceano: % chunks', ocean_chunks;
    RAISE NOTICE 'Selva: % chunks', jungle_chunks;
    RAISE NOTICE 'Floresta: % chunks', forest_chunks;
    RAISE NOTICE 'Total: % chunks', (desert_chunks + ocean_chunks + jungle_chunks + forest_chunks);
END
$$ LANGUAGE plpgsql;

-- Inserir alguns jogadores de exemplo (apenas se não existirem)
INSERT INTO Jogador (Nome, Vida_max, Vida_atual, xp, forca, Id_Chunk_Atual)
VALUES
    ('Player1', 100, 100, 0, 10, 1),
    ('Player2', 120, 120, 50, 12, 2),
    ('Player3', 110, 110, 25, 11, 3)
ON CONFLICT (Id_Jogador) DO NOTHING;

1.5 Vídeo de Demonstração

Assista ao nosso vídeo de demonstração aqui: Vídeo do módulo 2 no YouTube