exercício javaFx

Tópicos: máquina de estados, interface gráfica, threads,

o diagrama da máquina de estados:

o projeto:

o código:

package jfxsomajogo;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import jfxsomajogo.modelo.SomaJogoObs;
import jfxsomajogo.ui.SomaUI;

public class JFXSomaJogo extends Application {

    public static void main(String[] args) {
        launch(args);;
    }
    
    @Override
    public void start(Stage primaryStage){
        primaryStage.setTitle("Acereta nos números");
        SomaJogoObs a = new SomaJogoObs();
        SomaUI ui = new SomaUI(a);
        Scene scene = new Scene(ui, 300,250);
        primaryStage.setScene(scene);
        primaryStage.show();
     
        //que serve para fechar a janela
        primaryStage.setOnHidden((WindowEvent event) -> {
            a.paraRelogio();
        });
    }
    
}
package jfxsomajogo.fsm;

import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.modelo.SomaDados;

public class AguardaNomeJogador extends SomaFSMAdapter {

    public AguardaNomeJogador(SomaDados dados) {
        super(dados);
    }

    //transição
    @Override
    public ISomaFSM defineJogador(String nome) {
        dados.defineNomeJogador(nome);
        if (dados.jogadorValido()) {
            dados.reset(); //iniciação do jogo
            return new AguardaResposta(dados); //novo estado
        }else{
            return this;
        }
    }

    //cada um retorna o seu próprio identicador
    //para evitar o uso do instanceof e o get class
    @Override
    public EstadoID obtemEstadoID() {
        return EstadoID.AGUARDA_NOME;
    }

}

package jfxsomajogo.fsm;

import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.modelo.SomaDados;

public class AguardaReinicio extends SomaFSMAdapter {

    public AguardaReinicio(SomaDados dados) {
        super(dados);
    }

    //este estado é basicamente, um "press any key to conitnue"
    @Override
    public ISomaFSM recomeca() {
        return new AguardaNomeJogador(dados);
    }

    @Override
    public EstadoID obtemEstadoID() {
        return EstadoID.AGUARDA_REINICIO;
    }
}

package jfxsomajogo.fsm;

import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.modelo.SomaDados;

public class AguardaResposta extends SomaFSMAdapter{

    AguardaResposta(SomaDados dados) {
        super(dados);
    }

    @Override
    public ISomaFSM tentaResposta(int n) {
        dados.tentaResposta(n);
        if(dados.excedeuErros()){
            return new AguardaReinicio(dados); //muda de estado
        }else{
            return this; //fica no mesmo estado
            //return new AguardaResposta(dados); //alternativa, mostramos evolução do estado
        }
    }
    
    @Override
    public ISomaFSM relogioAvanca() {
        dados.diminuiCountDown();
        if(dados.excedeuErros()){
            return new AguardaReinicio(dados);
        }else{
            return this;
        }
    }

    @Override
    public EstadoID obtemEstadoID(){
        return EstadoID.AGUARDA_RESPOSTA; //para evitar o instance of
    }
}

package jfxsomajogo.fsm;

import java.io.Serializable;
import jfxsomajogo.integracao.EstadoID;

public interface ISomaFSM extends Serializable{
    //vai servir para definir um estado, transições:
    default ISomaFSM defineJogador(String nome){return this;} 
    //implementação default, pode ser no Adpater
    default ISomaFSM tentaResposta(int n){return this;}
    default ISomaFSM recomeca(){return this;}
    default ISomaFSM relogioAvanca(){return this;}
    EstadoID obtemEstadoID(); //retorna um ID
    
    //default, permite a implementação default mas devia ser no adaptar e não aqui.
    //mudar no final para ver como fica
}

package jfxsomajogo.fsm;

import jfxsomajogo.modelo.SomaDados;

public abstract class SomaFSMAdapter implements ISomaFSM{
    //guarda a referencia para o modelo de dados
    protected final SomaDados dados;
    //protected para os outros estados poderem aceder à informação
    
    protected SomaFSMAdapter(SomaDados dados){
        this.dados = dados;
    }
    
    //public AdividnhaDados obtemDados(){return dados;}
    //usa-se protected para isto
    
    //implementações de default de cada um dos métodos de 
    
}


package jfxsomajogo.integracao;
//isto é um package que vai ser usado pelo modelo de dados e pela interface


import jfxsomajogo.fsm.AguardaReinicio;

public enum EstadoID {
    //os ID dos estados
    AGUARDA_NOME, AGUARDA_RESPOSTA, AGUARDA_REINICIO
}

//este package agrupa as enumerações e constantes relacioandos com
//integração entre vários módulos da aplicação
//assim, a UD nao tem que improtar coisas dos packages de dados ou FSM
package jfxsomajogo.integracao;
//isto é um package que vai ser usado pelo modelo de dados e pela interface

public enum MsgCode {
    START, RIGHT, WRONG, TIMEOUT
}

package jfxsomajogo.integracao;
//isto é um package que vai ser usado pelo modelo de dados e pela interface

public enum PropsID {
    
    //podiam ser usadas constantes
    //"" -> alteração gráfica
    PROP_ESTADO("prop_estado"),
    PROP_DESAFIO("prop_desafio"),
    PROP_RELOGIO("prop_relogio");
    
    String valor;

    private PropsID(String v) {
        this.valor = v;
    }

    @Override
    public String toString() {
        return valor;
    }
}

package jfxsomajogo.modelo;

import java.io.Serializable;
import java.util.Random;
import jfxsomajogo.integracao.MsgCode;

public class SomaDados implements Serializable {

    private static final int COUNTDOWN_TIMEOUT = 10;
    private static final int MAX_ERROS = 3;
    private static final int MIN_VALOR = 1;
    private static final int MAX_VALOR = 100;

    private String jogador = "X";
    private MsgCode msg; //ver o estado actual
    private int pontuacao, erros;
    private int numA, numB;
    private int countdown;
    Random rand;

    SomaDados() {
        rand = new Random();
        jogador = "X";
        msg = MsgCode.START;
        pontuacao = erros = numA = numB = -1;
        countdown = -1;

    }

    public final void reset() {
        pontuacao = 0;
        erros = 0;
        novoDesafio();
        msg = MsgCode.START;
    }

    public void defineNomeJogador(String nome) {
        jogador = nome;
    }

    public void novoDesafio() {
        numA = rand.nextInt(MAX_VALOR - MIN_VALOR + 1) + MIN_VALOR;
        numB = rand.nextInt(MAX_VALOR - MIN_VALOR + 1) + MIN_VALOR;
        resetCountDown();
    }

    public void diminuiCountDown() {
        --countdown;
        if (countdown <= 0) {
            msg = MsgCode.TIMEOUT;
            erros++;
            novoDesafio();
        }
    }

    public void tentaResposta(int resp) {
        if (resp == (numA + numB)) {
            pontuacao++;
            msg = MsgCode.RIGHT;
            novoDesafio();
        } else {
            erros++;
            msg = MsgCode.WRONG;
        }
    }

    //para ver a evolução dos estados
    public boolean jogadorValido() {
        //se indicar o nome, pode ir para jogar
        return jogador != null && jogador.length() > 0;
    }

    public boolean excedeuErros() {
        return erros >= MAX_ERROS;
    }

    //obtem info para o ecrã
    public String obtemNomeJogador() {
        return jogador;
    }

    public int obtemNumA() {
        return numA;
    }

    public int obtemNumB() {
        return numB;
    }

    public int obtemPontuacao() {
        return pontuacao;
    }

    public int obtemErros() {
        return erros;
    }

    public int obtemCountDown() {
        return countdown;
    }

    public MsgCode obtemMsg() {
        return msg;
    }

    private void resetCountDown() {
        countdown = COUNTDOWN_TIMEOUT;
    }
}

package jfxsomajogo.modelo;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import jfxsomajogo.fsm.AguardaNomeJogador;
import jfxsomajogo.fsm.ISomaFSM;
import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.integracao.MsgCode;
import jfxsomajogo.integracao.PropsID;

//esta é a classe "contexto" da máquina de estados
//tem a máquina de estados + dados
//máquina de estados
public class SomaJogo implements Serializable {

    private final SomaDados dados = new SomaDados(); //criar o modelo de dados
    private ISomaFSM estado = null;  //define o estado incial

    public SomaJogo() {
        estado = new AguardaNomeJogador(dados);
    }

    //defineEstado recebe o próximoe stado
    private Set<PropsID> defineEstado(ISomaFSM prox, PropsID... eventosBase) {
        //isto vai ajudar no observable, vai avisar a vista
        Set<PropsID> eventos = new HashSet<>(Set.of(eventosBase));
        ISomaFSM anterior = estado;
        estado = prox;
        if (anterior != prox) {
            eventos.add(PropsID.PROP_ESTADO);
        }
        return eventos;
    }

    //operação do sistema ("dispositivo") contexto da FSM
    //-> reencaminha para FSM
    //os métodos que permitem a transição de estados
    //ponte entre o estado, o modelo de estados e a vista
    public Set<PropsID> defineJogador(String nome) {
        //ao inves de retornar o estado pode ser um return de boolean
        //retornar Set<PropsID>, um conjunto de situações de jogo que ajuda na vista do jogo
        //para a parte gráfica
        return defineEstado(estado.defineJogador(nome));
    }

    public Set<PropsID> tentaResposta(int n) {
        return defineEstado(estado.tentaResposta(n), PropsID.PROP_DESAFIO);
        //estas duas significa que podem acontecer dois eventos, e disparar o fire de uma das duas
    }

    public Set<PropsID> recomeca() {
        return defineEstado(estado.recomeca());
    }

    public Set<PropsID> relogioAvanca() {
        return defineEstado(estado.relogioAvanca(), PropsID.PROP_RELOGIO, PropsID.PROP_DESAFIO);
    }

       
    public EstadoID obtemEstadoID() {
        return estado.obtemEstadoID();
    }

    //obtenção de dados para uso nas UI
    //informação relevanta para mostrar ao utilizador
    //-> reencaminha para dados
    
    //isto é o mesmo que os gets apenas traudizdo para obtem
    //para serem mostrados no UI do utilziador
    
    public String obtemNomeJogador() {
        return dados.obtemNomeJogador();
    }

    public int obtemNumA() {
        return dados.obtemNumA();
    }

    public int obtemNumB() {
        return dados.obtemNumB();
    }

    public int obtemPontuacao() {
        return dados.obtemPontuacao();
    }

    public int obtemErros() {
        return dados.obtemErros();
    }

    public MsgCode obtemMsg() {
        return dados.obtemMsg();
    }

    public int obtemCountDown() {
        return dados.obtemCountDown();
    }
    
    //não devemos retornar o modelo SomaDados todo
    //porque se o fizermos vamos dar hipotese a quem faz
    //a vista de alterar os dados

}

package jfxsomajogo.modelo;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.integracao.MsgCode;
import jfxsomajogo.integracao.PropsID;

//missao principal - adicionar o suporte para ligacao a vistas
// = property change support
//mantem a ligacao separada do contexto da FGSM e dados
//permite save/load a FSM+dados sem dependencia (referencia) a vistas
//adiciona save/load neste nivel e age sobre o modelo (contexto da FSM)
public class SomaJogoObs {

    //os propertir change support
    SomaJogo jogo = null; //a referencia para o jogo, maquina de estados
    private PropertyChangeSupport props = null;

    private final Timer relogio;
    private final Temporizador segundos;

    public SomaJogoObs() {
        jogo = new SomaJogo();
        props = new PropertyChangeSupport(jogo);

        relogio = new Timer();
        segundos = new Temporizador();
        relogio.schedule(segundos, 0, 1000);
    }

    //registar o interesse das vistas, quando uma determinada propriedade for alterada
    //
    public void registaPropertyChangeListener(PropsID prop, PropertyChangeListener listener) {
        props.addPropertyChangeListener(prop.toString(), listener);
    }

    private void disparaEventos(Set<PropsID> eventos) {
        for (var e : eventos) {
            props.firePropertyChange(e.toString(), null, null);
        }

        //alternaiva ao for
        //eventos.forEach((e) -> {
        //props.firePropertyChange(e.toString(), null, null);
        //});
    }

    public void paraRelogio() {
        segundos.cancel();
        relogio.purge(); //eliminar todas as as timer text 
        relogio.cancel();
    }

    // inicio: métodos que fazem a ponte entre as vistas e a maquina de estados
    public void relogioAvanca() {
        disparaEventos(jogo.relogioAvanca());
    }

    // operação do sistema ("dispotivo") - reencaminha para jogo depois p/ FSM
    public void defineJogador(String nome) {
        disparaEventos(jogo.defineJogador(nome));
    }

    public void tentaResposta(int n) {
        disparaEventos(jogo.tentaResposta(n));

        //alternaiva ao udo do disparaEventos
        //jogo.tentaResposta(n);
        //props.firePropertyChange(PropsID.PROP_DESAFIO, null. null);
        //props.firePropertyChange(PropsID.PROP_ESTADO, null. null);
    }

    public void recomeca() {
        disparaEventos(jogo.recomeca());
    }

    public EstadoID obtemEstadoID() {
        return jogo.obtemEstadoID();
    }
    //fim: métodos que fazem a ponte entre as vistas e a maquina de estados

    //obenção de dados para uso das UI - reencaminha p/jogo (depois para dados)
    public String obtemNomeJogador() {
        return jogo.obtemNomeJogador();
    }

    public int obtemNumA() {
        return jogo.obtemNumA();
    }

    public int obtemNumB() {
        return jogo.obtemNumB();
    }

    public int obtemPontuacao() {
        return jogo.obtemPontuacao();
    }

    public int obtemErros() {
        return jogo.obtemErros();
    }

    public MsgCode obtemMSG() {
        return jogo.obtemMsg();
    }

    public int obtemCountDown() {
        return jogo.obtemCountDown();
    }

    //load/save
    public void saveGame(String filename) {
        ObjectOutputStream out;
        try {
            out = new ObjectOutputStream(new FileOutputStream(filename));
            out.writeObject(jogo);
            out.close();
        } catch (IOException e) {
            Logger.getLogger(SomaJogoObs.class.getName()).log(Level.SEVERE, null, e);
        }
    }

    public void loadGame(String filename) {
        ObjectInputStream in;
        try {
            in = new ObjectInputStream(new FileInputStream(filename));
            jogo = (SomaJogo) in.readObject();
            in.close();
            props.firePropertyChange(PropsID.PROP_ESTADO.toString(), null, null);
        } catch (IOException | ClassNotFoundException e) {
            Logger.getLogger(SomaJogoObs.class.getName()).log(Level.SEVERE, null, e);
        }
    }

    //time taks
    class Temporizador extends TimerTask {
        @Override
        public void run() {
            relogioAvanca();
        }
    }
}

package jfxsomajogo.ui;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.integracao.MsgCode;
import jfxsomajogo.integracao.PropsID;
import jfxsomajogo.modelo.SomaJogoObs;


//esta é uma classe genérica, que é um adpater para PropertyChangeListener
//PropertyChangeListenerJFXAdapter faz com que seja implementado o PropertyChangeListener
//vai implementar o propertyChange, que é o método obrigatório pelo inteface método PropertyChangeListener
//e sempre que ele for chamado, e antes de correr o código pretendido ele faz um  
// Platform.runLater(() do método onChange(evt);
// e quanto ele for corrido ele já foi corrido no contexto do .runLater(()
//com o objecto qd é criado é abstracto 
//por exemplo propsID.PROP_ESTADO, new PropertyChangeListenerJFXAdapter() 
//sou obrigado a implementar o onChange(), com todo o código necessário
//para as alterações que eu necessito 
//e assim o onChange() já foi executado na thread do javaFX
//evito assim que estoire, e que acontece quanto tentamos executar código a partir de uma thread
//difernte da que está responsavel por fazer a gestão dos componentes gráficos
//isto por causa da utilização do timer, porque o timer está numa thread à parte


abstract class PropertyChangeListenerJFXAdapter implements PropertyChangeListener{
    @Override
    public final void propertyChange(PropertyChangeEvent evt){
        Platform.runLater(()->{
            onChange(evt);
        });
    }
abstract public void onChange(PropertyChangeEvent evt);
}


public class SomaUI extends BorderPane {

    SomaJogoObs adivObs;

    public SomaUI(SomaJogoObs a) {
        adivObs = a;
        FileChooser fileChooser = new FileChooser();

        //esta funcao esta extensa. passar código para funcções auxiliares
        //implica passar as refs locais dos componentes para a classe
        //cria componentes
        //menu
        MenuBar menuBar = new MenuBar();

        Menu fileMenu = new Menu("file");
        MenuItem fileNew = new MenuItem("new");
        MenuItem fileSave = new MenuItem("save");
        MenuItem fileLoad = new MenuItem("load");
        MenuItem fileExit = new MenuItem("exit");
        fileMenu.getItems().addAll(fileNew, fileSave, fileLoad, fileExit);

        Menu aboutMenu = new Menu("about");
        MenuItem aboutInfo = new MenuItem("info");
        aboutMenu.getItems().addAll(aboutInfo);

        menuBar.getMenus().addAll(fileMenu, aboutMenu);
        setTop(menuBar);

        //eventos para os menus
        //file->new
        fileNew.setOnAction((ActionEvent e) -> {
            adivObs.recomeca();
        });
        //file-> save
        fileSave.setOnAction((ActionEvent e) -> {
            File f = fileChooser.showSaveDialog(getScene().getWindow());
            if (f != null) {
                adivObs.saveGame(f.getAbsolutePath());
            }

        });

        //file -> load
        fileLoad.setOnAction((ActionEvent e) -> {
            File f = fileChooser.showSaveDialog(getScene().getWindow());
            if (f != null) {
                adivObs.loadGame(f.getAbsolutePath());
            }

        });

        //file -> exit
        fileExit.setOnAction((ActionEvent e) -> {
            encerra();
            ((Stage) getScene().getWindow()).close();

            //Platform.exit(); //funciona apenas em windows
        });
        //about
        aboutInfo.setOnAction((ActionEvent e) -> {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setHeaderText("acerca disto");
            alert.getDialogPane().setContentText("programa de javafx");

            alert.showAndWait();
        });

        StackPane areaUtil = new StackPane();
        areaUtil.setPadding(new Insets(10));

        UINome uinome = new UINome(); //é um pane
        UIJoga uijoga = new UIJoga(); //é um pane
        UIFim uifim = new UIFim();

        uijoga.setVisible(false);
        uifim.setVisible(false);

        areaUtil.getChildren().addAll(uinome, uijoga, uifim);

        setCenter(areaUtil);
    }

    public void encerra() {
        adivObs.paraRelogio();
    }

    class UINome extends VBox {

        private final Label lNome = new Label("escreve nome");
        private final TextField tfNome = new TextField();
        private final Button btnDefineNome = new Button("ok");

        public UINome() {
            super(10); //espaçamento entre items
            adivObs.registaPropertyChangeListener(PropsID.PROP_ESTADO, new PropertyChangeListenerJFXAdapter() {
                //registaPropertyChangeListener para sermos avisados de evolução de estados
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    setVisible(adivObs.obtemEstadoID() == EstadoID.AGUARDA_NOME);
                }
            });
            btnDefineNome.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent t) {
                    adivObs.defineJogador(tfNome.getText());
                }
            });
            tfNome.setOnKeyPressed(e -> {
                if (e.getCode() == KeyCode.ENTER) {
                    adivObs.defineJogador(tfNome.getText());
                }
            });
            getChildren().addAll(lNome, tfNome, btnDefineNome);

        }

    }

    class UIJoga extends VBox {

        private final Label lMsg = new Label();
        private final Label lDesafio = new Label();
        private final TextField tfResposta = new TextField();
        private final Button btnEnviaResposta = new Button("ok");
        private final Label lRelogio = new Label();
        Map<MsgCode, String> mensagens = new HashMap<>();

        public UIJoga() { //1,39
            super(10); //espaçamento entre items
            mensagens.put(MsgCode.START, "vamos começar");
            mensagens.put(MsgCode.RIGHT, "certo. outro desafio");
            mensagens.put(MsgCode.WRONG, "errado. tenta outra vez");
            mensagens.put(MsgCode.TIMEOUT, "tempo excedido. outro desafio");

            btnEnviaResposta.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent t) {
                    tentaResposta();
                }
            });
            tfResposta.setOnKeyPressed(e -> {
                if (e.getCode() == KeyCode.ENTER) {
                    tentaResposta();
                }
            });

            adivObs.registaPropertyChangeListener(PropsID.PROP_ESTADO, new PropertyChangeListenerJFXAdapter() {
                //registaPropertyChangeListener para sermos avisados de evolução de estados
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    if (adivObs.obtemEstadoID() == EstadoID.AGUARDA_RESPOSTA) {
                        setVisible(true);
                        defineMensagens(); //atualziar todas as labels com o texto em causa
                        intiTfResposta(); //por o texto a vazio (escrita do texto), quando existe mudança de estado
                    } else {
                        setVisible(false);
                    }
                }
            });

            adivObs.registaPropertyChangeListener(PropsID.PROP_DESAFIO, new PropertyChangeListenerJFXAdapter() {
                //registaPropertyChangeListener para sermos avisados de evolução de estados
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    defineMensagens();
                }
            });

            adivObs.registaPropertyChangeListener(PropsID.PROP_RELOGIO, new PropertyChangeListenerJFXAdapter() {
                //registaPropertyChangeListener para sermos avisados de evolução de estados
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    //para processamento futuro caso necessário
                }
            });

            getChildren().addAll(lMsg, lDesafio, tfResposta, btnEnviaResposta, lRelogio);
            defineMensagens();
        }

        private void defineMensagens() {
            lMsg.setText(mensagens.getOrDefault(adivObs.obtemMSG(), "nao compreendo.."));
            //adivObs.obtemMSG() com ajuda de um switch case para obter as mensagens (string) acerca do estado
            //em que se encontra
            //a alternativa foi a opção de se usar um Map<MsgCode, String> mensagens

            lDesafio.setText(adivObs.obtemNomeJogador()
                    + " ,quando é "
                    + adivObs.obtemNumA()
                    + " + "
                    + adivObs.obtemNumB() + "?"
            );
            lRelogio.setText("tempo restante: " + adivObs.obtemCountDown());
        }

        private void intiTfResposta() {
            //serve para apagar a resposta que é escrita
            //e só acontece quando existe mudança de estado
            tfResposta.setText("");
            tfResposta.requestFocus();

        }

        private void tentaResposta() {
            try {
                int val = Integer.parseInt(tfResposta.getText());
                adivObs.tentaResposta(val);
                intiTfResposta();

            } catch (NumberFormatException e) {

            }
        }
    }

    class UIFim extends VBox {

        private final Label lResultado = new Label();
        private final Button btnRecomecar = new Button("Novo jogo");

        public UIFim() {
            super(5);
            adivObs.registaPropertyChangeListener(PropsID.PROP_ESTADO, new PropertyChangeListenerJFXAdapter() {
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    if (adivObs.obtemEstadoID() == EstadoID.AGUARDA_REINICIO) {
                        setVisible(true);
                        lResultado.setText(adivObs.obtemNomeJogador()
                                + ", o teu resultado:\n"
                                + "pontuação: "
                                + adivObs.obtemPontuacao()
                                + "\n"
                                + "Erros:" + +adivObs.obtemErros()
                                + "(obviamente)\n\n"
                                + "ok para jogar outra vez\n\n"
                        );
                        //formatação com \n em ambiente grafico - tem tudo a ver
                    } else {
                        setVisible(false);
                    }
                }
            });
            btnRecomecar.setOnAction((ActionEvent e) -> {
                adivObs.recomeca();
            }
            );
            getChildren().addAll(lResultado, btnRecomecar);
        }

    }

}

Tags : ,

JavaFX, exercício 3

package recursopa;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import recursopa.iu.gui.PaneOrganizer;

public class RecursoPA extends Application {
    public static void main(String[] args) {
        //passamos os argumentos para o subsistema gráfico os parametros
        launch(args); 
    }
 
    @Override
    //quando começa a aplicação
    public void start(Stage stage) throws Exception{
        PaneOrganizer po = new PaneOrganizer();
        Scene scene = new Scene(po,600,400);
        stage.setScene(scene); //aplica a cena no stage
        stage.setTitle("Exercicio de programação avançada");
        stage.setMinHeight(300.0);
        stage.setMinWidth(300.0);
        stage.show();
    }
}   
package recursopa.iu.gui;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import recursopa.logica.Figura;

public class PaneOrganizer extends Pane {
    //vai extender sempre um dos containers possiveis,  extends Pane

    Figura fig;
    Ellipse elipse;

    public PaneOrganizer() {
        fig = new Figura();
        fig.setPrimeiroPonto(0, 0);
        fig.setSegundoPonto(100, 50);
        fig.setRGB(0, 50, 0);

        criaLayout(); //para criar os componentes que vão surgir no ecrã
        registaListeners(); // registar os comportamentos dos objectos
        atualizaElipse();

    }

    void criaLayout() {
        //adicionar o elemento ao Pane
        elipse = new Ellipse();
        this.getChildren().add(elipse);
    }

    public void registaListeners() {
        //detectar as ações do rato
        this.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent t) {
                fig.setPrimeiroPonto((int) t.getX(), (int) t.getY());
                fig.setSegundoPonto((int) t.getX(), (int) t.getY());
                fig.setRGB((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256));
                atualizaElipse();
            }
        });

        //usando lambda functions
        this.setOnMouseReleased((t) -> {
            fig.setSegundoPonto((int) t.getX(), (int) t.getY());
            atualizaElipse();
        });
        this.setOnMouseDragged((t) -> {
            fig.setSegundoPonto((int) t.getX(), (int) t.getY());
            atualizaElipse();
        });

    }

    //para desenhar a elipse
    void atualizaElipse() {
        //elipse.setFill(Color.GREEN);
        elipse.setFill(Color.rgb(fig.getR(), fig.getG(), fig.getB())); //para ir buscar as informações à fig
        //elipse.setCenterX(200.0);
        elipse.setCenterX((fig.getXi() + fig.getXf()) / 2);
        //elipse.setCenterY(200.0);
        elipse.setCenterY((fig.getYi() + fig.getYf()) / 2);
        //elipse.setRadiusX(150.0);
        elipse.setRadiusX(fig.getLargura() / 2);
        //elipse.setRadiusY(100.0);
        elipse.setRadiusY(fig.getAltura() / 2);
    }

}

package recursopa.logica;
public class Figura {
//métodos necessários para gerir a figura

    int xi, yi, xf, yf; //posicao e tamanho
    int r, g, b; //para a cor

    public Figura() {

    }

    public int getXi() {
        return xi;
    }

    public int getYi() {
        return yi;
    }

    public int getXf() {
        return xf;
    }

    public int getYf() {
        return yf;
    }

    public int getR() {
        return r;
    }

    public int getG() {
        return g;
    }

    public int getB() {
        return b;
    }

    public void setRGB(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    public void setPrimeiroPonto(int x, int y) {
        this.xi = x;
        this.yi = y;
    }

    public void setSegundoPonto(int x, int y) {
        this.xf = x;
        this.yf = y;
    }

    public int getLargura() {
        return Math.abs(xf - xi); //não interessa a ordem dos valores
    }

    public int getAltura() {
        return Math.abs(yf - yi); //não interessa a ordem dos valores
    }
}

Tags : ,

JavaFX, exercícios

Relembrar para o netBeans correr um projeto com JavaFX e nas propriedades de um projeto:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class RecursoPA extends Application {
    public static void main(String[] args) {
        //passamos os argumentos para o subsistema gráfico os parametros
        launch(args); 
    }
 
    @Override
    //quando começa a aplicação
    public void start(Stage stage) throws Exception{
        BorderPane bp = new BorderPane();
        Label label1 = new Label("programação avançada");
        Label label2 = new Label("outra label");
        bp.setBottom(label1);
        bp.setTop(label2);
        //criar a cena
        Scene scene = new Scene(bp,400,400); //raiz da cena, e dimensões
        //adicionar a cena ao palco
        stage.setScene(scene);
        stage.show();
    }
}   
Tags : ,

Mais um canal.. com o Godot

Encontrei este canal com vídeos recentes e interessantes acerca do desenvolvimento de um videojogos com o motor Godot.

+infos(youtube): LINK

Tags : , ,

Várias propostas (em Vila Nova de Gaia)

A Saber Interactive Porto está a contratar para várias posições.

+infos(rede social): LINK

Tags : ,

5 Tips for awesome Game Design Docs

Da índia surge um developer que dá algumas dicas acerca dos Game Design Docs:
#1 always make a revision history and table of contents
#2 use mockups sketches references
#3 create asset lists
#4 customize your GDD
#5 update your GDD

+infos(o video): https://youtu.be/dgaUiGF9mkI

Tags : , ,

Assets de batalhas (WW)

A malta da Synty Store disponibilizou mais um pack de batalha e desta vez com referência à primeira guerra mundial. Para se ter acesso a este pack é necessário já ter o pack de guerra e o custo final de tudo é de:
POLYGON – War Pack – 80 euros
POLYGON – War Map – WWI – 18 euros

Assim e pelo POLYGON – War Map – WWI:

POLYGON – War Pack

+infos(War Map – WWI): https://syntystore.com/products/polygon-war-map-wwi

+infos(War Pack): https://syntystore.com/products/polygon-war-pack

Tags : , ,

“Os jogos de tabuleiro portugueses querem sentar o mundo à mesa”

E por último, para já, mais um texto muito interessante para arquivo.

Os jogos de tabuleiro portugueses querem sentar o mundo à mesa por Micael Sousa

“Há quem diga que os jogos de tabuleiro estão a fazer um regresso em grande e as estatísticas comprovam-no: em Portugal, as vendas aumentaram 12% no ano passado. As editoras portuguesas aproveitam o ímpeto e fazem pela internacionalização dos seus produtos — nas casas e cafés do resto do globo.

A associação Cidade Curiosa não fez por menos: pôs gente a jogar, jogando com o tempo. O grupo juvenil de Braga organizou, pela primeira vez, um evento para quem gosta de jogos de tabuleiro. E fê-lo de graça e para todos, já que o nível de dificuldade variava entre jogos e salas. Pediram parte do nome emprestado aos romanos e organizaram a Augusta Con, que se realizou entre 7 e 9 de Dezembro. Instalaram-se num local construído muito depois daquele império, o Mosteiro de Tibães. Pelo meio, deram aos pais a oportunidade de mostrar aos filhos jogos marcantes de outros anos e contornaram os ponteiros do relógio: é que à hora do almoço do último dia, um domingo, esperando-se alguma calma, ainda havia muita gente a jogar e a chegar para o fazer. “Só ontem passaram aqui mais de 1100 pessoas”, contava ao P3 Alberto Pereira, fundador da Cidade Curiosa Associação (CCA), nessa altura.

Não escondia a incredulidade, já que esperava “umas 300 pessoas por dia”. Por isso, os seus passos eram rápidos, e ia subindo e descendo vezes sem conta as escadas de acesso aos três pisos disponíveis. A qualquer altura parava para explicar o funcionamento de qualquer jogo aos mais inexperientes — tal como os restantes voluntários e membros da CCA. “É um trabalho de quatro anos que resulta neste sentido de comunidade”, explicou. E, na verdade, é este o termo que melhor descreve o conjunto de amantes de jogos de tabuleiro, que corre congressos do género apenas para jogar. Como Javier, de Madrid, que para aqui estar faltou ao Game On, outro evento dedicado à temática que decorreu em simultâneo na capital espanhola. “Fica a reserva para o próximo congresso!”, antecipa o espanhol, lançando o aviso a Alberto à despedida. Atrás dos dois, um cartaz vermelho com letras brancas indicava o espaço dedicado à MEBO Games, editora lisboeta de jogos que ali apresentou a sua mais recente criação, o Arraial.

Agora, um convite ao leitor, bem ao jeito dos jogos de tabuleiro: atire-se o dado e saltemos umas quantas casas, que o local é Lisboa. Bónus: o peão é um telefone. Do lado de lá fala Gil D’Orey, 40 anos, fundador da MEBO Games. Da publicidade passou para os jogos de tabuleiro, que já faziam parte da sua vida “desde miúdo”. “Sempre achei graça àqueles clássicos, como o xadrez. Na altura, não havia muita escolha em Portugal.” Agora, diversidade é o que não falta na oferta da editora: há o Viral, no qual o jogador é um vírus, ou o Sagrada, que nos transforma em vitralistas na Sagrada Família, em Barcelona O Viral faz jus ao nome, tendo já versão russa. Quanto ao Arraial, podem esperar-se versões em inglês, coreano, mandarim e japonês.

Jogar é inclusão
Diz Gil que a MEBO se distingue pelo tema predominante dos jogos: a História portuguesa. Neste âmbito, há Caravelas II, o Castelo de São Jorge, Reis de Portugal e, claro, o Arraial — um jogo que desafia os participantes a criarem a melhor festa para a sua rua. As propostas “são para todas as faixas etárias e feitios”. O criador da editora defende que os jogos “devem ser jogados entre pais e filhos, e não para estupidificarem os pais”. O mesmo tema inspira a Pythagoras, editora sediada em Vermoil, Pombal, que faz da sua oferta um manual de História de Portugal, através de cartas, puzzles e jogos.

O novo jogo da CardsLab, a terceira edição da linha Países, segue o mesmo raciocínio: igualar as aptidões dos jogadores. Nele, os participantes têm de responder a questões sobre geografia. “O objectivo é juntar várias faixas etárias, várias gerações. Das últimas vezes que testamos o jogo, os miúdos ganharam sempre”, conta Pedro Gordalina, 35 anos, o fundador da empresa instalada em Lisboa. O também consultor informático só estava à espera “da altura certa para avançar”. Isso aconteceu em 2016, quando a startup ficou em 3.º lugar no Novabase Gameshifters, um evento que premeia ideias de diversos ramos e que impulsiona novos negócios. Depois, fizeram duas versões do jogo, testando-o em escolas, “sem grande divulgação”.

Hoje, a estratégia é diferente: querem levar a brincadeira a todos os lados e a “todos os tipos de ambiente”. Por essa razão, os produtores portugueses de jogos de tabuleiro têm sempre uma preocupação maior: a parte lúdica. “Todos os jogos são educativos, mas, para que essa parte funcione, têm de ter uma mecânica apelativa”, argumenta o fundador da MEBO Games. Assim, roubam-se uns minutos aos gadgets. O que também pode acontecer no trabalho – e para “melhorar a performance”, como diz Micael Sousa. O investigador da Escola Superior de Tecnologia e Gestão de Leiria, de 36 anos, especifica a questão: os party games “podem ajudar a criar e a produzir”, ao passo que os mais sérios “potenciam o desenvolvimento de valências”.

A idade de ouro dos jogos de tabuleiro
A CardsLab “já entrou no grande retalho”, e o novo jogo tem sido apresentado em diversos sítios — das superfícies comerciais às livrarias. A MEBO Games, por seu lado, fez chegar o jogo de espiões Estoril 1942 à China. “É um jogo português sobre algo passado numa cidade portuguesa a chegar à China, para além de ser um sucesso em Portugal”, diz Gil D’Orey. A Pythagoras, por seu turno, vai vender os direitos de Lusitânia a três países: Espanha, EUA e Inglaterra. São casos de sucesso como aqueles de que já aqui falámos: a Lisboa, de Vital Lacerda, e A Aldeia Adormece, de André Santos.

Não há concorrência com os videojogos que se amontoam prateleira acima nos supermercados: “O mercado é distinto”, começa por explicar Pedro Gordalina. “Uma das grandes vantagens dos jogos de tabuleiro é mitigar a exclusão social e trazer para a mesa uma família que está no sofá ao telemóvel.” Ou então “tornar-nos mais humanos”, como defende Gil D’Orey.

Assim, não é de estranhar o sucesso que têm vindo a recolher, e a culpa é toda dos millennials. Pelo menos é o que diz o Guardian, que procurou justificações para a multidão (40 mil pessoas, para precisar) que se esperava, em Maio passado, na feira de jogos de tabuleiro UK Games Expo. Em Portugal, segundo a GfK, empresa de estudos de mercado, as vendas de jogos de tabuleiro (e puzzles) aumentaram 12% em 2017, e quase dois milhões de unidades saíram das prateleiras para os carrinhos. Segundo a Research and Market, espera-se que, entre 2018 e 2022, haja uma subida de 23% no mercado dos jogos de tabuleiro em todo o mundo (ainda que este crescimento esteja menos acelerado do que noutros anos).

Resultado de revivalismos? Gil D’Orey acha que não. “Os jogos são muito modernos e apelam. O produto é actual, mas não usa a parte electrónica de propósito”, explica. Dá o exemplo da SPIEL, a feira anual de jogos de Essen, Alemanha, considerada a “maior do mundo”: “Este ano, foram apresentados lá mil jogos novos.” E se fosse um revivalismo, “os jogos antigos vinham ao de cima”, que não é o que acontece: “Os jogos de hoje não têm nada que ver com os antigos. Só o cartão!” Na opinião de Micael Sousa, há uma espécie de regresso “da actividade social na era digital, que aproxima as pessoas, para mexer em coisas e estar com pessoas”. Ao mesmo tempo, podem resgatar-se memórias à mesa — que não são só as de casa.

O que não faltam são listas de cafés onde se pode fazer sala a jogar, por exemplo, o Catan. Mas isso é lá fora; em Portugal, o único estabelecimento do género (para já) conhecido fica em Vila do Conde. No A Jogar É Que a Gente Se Entende, há quase 900 opções que não ganham pó nas estantes, e o pensamento de inclusão repete-se: qualquer um sabe jogar algum dos jogos que ali estão — e não há só o Monopólio. Os proprietários, Manuel e Dina Silva, deixaram Londres e as suas carreiras para voltarem a Vila do Conde com este objectivo. “Era uma ideia de adolescência, porque os jogos estiveram sempre presentes”, explica Manuel Silva. “E também queria ‘reformar-me’ antes dos 35. Consegui, fi-lo aos 32.” Dina, com 34, também. Não é bem um trabalho, porque tem muito de passatempo pelo meio. “As pessoas que cá param, muitas vezes, não sabem o que jogar, e eu ajudo-as. Se não conseguir, vão logo buscar o Scrabble ou o Monopólio”, conta. E no café entra toda a gente: crianças, adultos, idosos e universitários. Porque os jogos, dentro ou fora de portas de casa, já não são só para nerds.

Alguns jogos portugueses para pôr na mesa:
Caravelas II – Navegar pelas rotas comerciais mais importantes que ergueram o Império Português, reunindo especiarias e metais preciosos. Cuidado: há um Adamastor à espreita. Para dois ou quatro jogadores, a partir dos oito anos de idade. Da MEBO Games, por 19,90 euros.
Lusitânia – A Pythagoras propõe uma viagem no tempo. Os romanos perderam outra batalha na Lusitânia. O povo “dos confins da Ibéria” tenta resistir novamente. Podem jogar até quatro pessoas, maiores de oito anos. O preço é de 13,95 euros.
Países – Um teste ao conhecimento geográfico para todas as idades. Há missões e cartas especiais. Para jogar a dois – ou a seis. Por 18,90 euros, pela CardsLab Games.
Gente Com Memória – Jogo de cartas da Pythagoras, ao estilo do “peixinho”. O objectivo é reunir quatro personalidades da História portuguesa da mesma família. Para famílias, opondo duas equipas de dois jogadores. Preço: 9,99 euros.
Lixo? – Para ensinar a reciclagem aos mais novos (e aos mais velhos). Toda a família pode jogar, mas o limite é de seis jogadores. A MEBO Games vende o jogo por 14,90 euros.
A Aldeia Adormece – Há um mistério da aldeia do Talasnal. Estamos em 1911. O jogo é do Estaminé Studio. Pode participar uma aldeia inteira: o número de jogadores pode chegar aos 25. Preço sobre consulta.
Passa o Desenho – Um party game onde desenhamos as palavras que nos calham na rifa e tentamos adivinhar os desenhos dos adversários. O número mínimo de participantes é quatro; o máximo, oito. Disponível na MEBO Games a 29,90 euros.
Millions, o Último Soldado – Mais um jogo de cartas da Pythagoras. O protagonista é o Soldado Milhões, o “herói português da Primeira Guerra Mundial”. As cartas têm pontos positivos e negativos, e ganha quem tiver mais pontos. Podem jogar até cinco pessoas. O Millions está à venda por 14,95 euros.”

+infos(fonte): LINK

Tags : ,

“Os jogos de tabuleiro não são todos iguais”

Mais um artigo por seguimento do anterior, interessante e para arquivo acerca dos jogos de tabuleiro

Os jogos de tabuleiro não são todos iguais por Micael Sousa

“Há toda uma nova cultura de (board)gaming que veio para ficar. Estimam-se taxas de crescimento sucessivas de mais de 20% nos últimos dez anos. Mas, afinal, que jogos são estes? Será revivalismo e o voltar aos clássicos?
Andam por aí uns jogos de tabuleiro a atrair multidões crescentes. Podia pensar-se que era uma moda passageira, mas o crescimento de vendas, de publicações e de eventos contraria isso. É toda uma nova cultura de (board)gaming que veio para ficar. Estimam-se taxas de crescimento sucessivas de mais de 20% nos últimos dez anos. Mas, afinal, que jogos são estes? Será revivalismo e o voltar aos clássicos?

Estes novos jogos tendem a ser apelidados de modernos mas, provavelmente, o termo mais correcto seria jogos de hobby. Isto porque estão associados a uma cultura própria, mas pertencendo ao universo geek. Sabemos que, por volta dos anos 50, especialmente nos Estados Unidos e no Reino Unido, mas também depois na Europa Ocidental, a economia de mercado e a sociedade de consumo geraram tempo e dinheiro para uma maior dedicação ao lazer. Alguns entusiastas pegaram nos modelos dos jogos de guerra analógicos, muito utilizados pelos exércitos durante o século XIX e meados do século XX, para criar novos jogos lúdicos. Surgiram os primeiros wargames, depois simuladores temáticos, role play games e os chamados eurogames, muito associados ao fenómeno cultural alemão. Os jogos de cartas coleccionáveis são igualmente importantes nesta história de tendências. Hoje, os jogos de mesa são também híbridos, conjugando o aspecto inimitável da interacção humana presencial com a automatização de tarefas e geração de elementos visuais e sonoros que ajudam a criar ambientes mais imersivos.

Vamos então explorar um pouco mais os vários tipos de jogos de hobby. Os wargames tendem a ser jogos de duelos em que se simulam complexas batalhas. Os role play games (RPG) baseiam-se nas narrativas, com o Dungeons & Dragons como grande impulsionador do género, onde personagens entram em mundos fantasiosos de aventuras. Mas há RPG com muitos outros temas. Depois temos duas vertentes que, actualmente, tendem a múltiplas fusões: os Eurogames e os American games. Nos Eurogames de inspiração alemã surgem sistemas estratégicos de planeamento e eficiência, normalmente associados a gestão e planeamento de recursos, mas com muita variedade de mecânicas e formatos, em constantes processos de inovação. Os American games, conhecidos no hobby por ameritrash, tendem a ser mais temáticos, recorrendo a mecânicas de jogo mais tradicionais do que o refinamento dos sistemas dos Eurogames e a incluir mais elementos aleatórios.

No entanto, os American games lidam melhor com as narrativas e imersões nos temas que abordam, por isso lhes chamam temáticos. Não esquecer também os jogos de cartas coleccionáveis, sendo Magic: The Gathering o mais conhecido de todos. Esses jogos recorrem a cartas de raridade variável, que podem atingir valores monetários impressionantes em mercados de transacções próprios, para construir baralhos e sistemas próprios de jogo, tendendo para o duelo entre jogadores. De notar que todos estes jogos de hobby são assumidamente produtos de autor, tanto no design dos mecanismos do jogo como da arte gráfica e de modelação de componentes.

Esta breve descrição dos jogos de hobby — aqueles que levaram recentemente 209 mil pessoas de todo o mundo à Essen Spiel na Alemanha e que vão levar muitas centenas também à Invictacon no Porto, de 8 a 10 de Novembro — serve para demonstrar como são diferentes dos jogos tradicionais, dos jogos clássicos (xadrez, damas, dominó, etc.) e dos jogos de massas (Monopólio, Cluedo, Risco). Também têm pouco que ver com alguns jogos criados por empresas e celebridades conhecidas da televisão, rádio e outros palcos mediáticos, que pouco ou nada tendem a saber sobre o design de jogos de hobby. Nesses casos limitam-se a usar marcas estabelecidas e a replicar mecânicas de outros jogos, tendencialmente jogos do mercado de massa, criando, na prática, produtos pouco inovadores.

Por isso, quando se depararem com esses títulos, desconfiem. Não são esses os jogos que estão a gerar inovação e entusiasmo pelo mundo fora. Não são esses jogos que contribuem para a era de ouro ou de renascimento dos jogos de tabuleiro. Aproveitem para experimentar, em alternativa, os jogos de hobby. Vão ficar maravilhados.”

+infos(fonte): LINK

Tags : ,

“Jogos de tabuleiro à distância e a solo, em tempos de covid-19”

Um artigo interessante e que fica para arquivo:
Jogos de tabuleiro à distância e a solo, em tempos de covid-19 por Micael Sousa

O uso dos jogos de tabuleiro em modo solitário também tem crescido. Apesar da principal razão para se jogar um jogo de tabuleiro ser a interacção pessoal presencial, que enriquece a experiência de jogo, existem jogos que podem ser jogados a solo ou outros que foram concebidos para isso mesmo.”

Os jogos de tabuleiro modernos estavam em fase de crescimento exuberante, apresentando tendências que iam além dos dois dígitos, continuando a bater recordes de angariação no Kickstarter. Ainda há pouco tempo o Frosthaven reuniu mais de 12 milhões de dólares (cerca de 10,7 milhões de euros)!

Para além do crescimento de vendas de jogos de tabuleiro em tempos de covid-19, como forma de encontrar mais uma actividade coletiva para fazer em casa, surgiu um novo fenómeno. Já existiam plataformas online onde se podiam jogar os mais conhecidos jogos de tabuleiro de hobby, quer seja com sistemas de automatização do sistema de jogo e possibilidade de jogar de forma assíncrona como no Board Game Arena, ou noutros mais flexíveis como o Tabletop Simulator e o Tabletopia, em que os jogadores têm de movimentar virtualmente os componentes de cada jogo. A plataforma Steam tem agora jogos de tabuleiro em formato digital e alguns dos jogos mais conhecidos também se encontram disponíveis nas consolas ou como aplicações de smartphone. Todas estas alternativas cresceram com a nova procura gerada pelo confinamento e isolamento social, especialmente as plataformas onde é possível jogar com outros jogadores, enquanto se conversa e interage à distância. Assim, os jogadores de hobby, que antigamente se encontravam nas sessões semanais presenciais públicas por todo o país, ou então em casa de amigos, passaram a recorrer ainda mais a estas plataformas, habitualmente apoiadas pelo Discord como espaço de comunicação. Neste momento, há vários grupos de Discord dedicados aos jogos de tabuleiro, entre eles os encontros semanais da comunidade Boardgamer Portugal. Mas existem muitos outros grupos semelhantes activados por comunidades locais pelo país fora.

Para além disto surgiu, fruto do engenho e imaginação, a utilização de plataformas online de videoconferência, como o Zoom, para jogar jogos de tabuleiro. Com a devida adaptação é possível jogar alguns destes jogos, tentando uma aproximação às vantagens da interacção pessoal que acontece nos jogos físicos. Na verdade, não é a mesma coisa, longe disso. Mas, enquanto não podemos voltar a estar à mesa, é mais uma possibilidade.

O uso dos jogos de tabuleiro em modo solitário também tem crescido. Apesar da principal razão para se jogar um jogo de tabuleiro ser a interacção pessoal presencial, que enriquece a experiência de jogo, existem jogos que podem ser jogados a solo ou outros que foram concebidos para isso mesmo. Temos jogos, a título de exemplo, como o 7th Continent ou o Robinson Crusoe, que encaixam nesse tipo de jogos onde um jogador pode partir à aventura para explorar o universo analógico associado a cada jogo. Temos também modos de jogo activados por sistemas automáticos em que se simulam formas de inteligência artificial através de baralhos de cartas. Mas podemos recorrer igualmente aos jogos mais simples, que geram continuamente novos puzzles para os jogadores resolverem, com alguns deles a poderem ser jogados gratuitamente e acedidos através de versões print & play, ou seja, para imprimir e jogar em casa. Esta é outra tendência forte do momento: poder imprimir o próprio jogo, que muitas vezes é disponibilizado gratuitamente; caso disso é o recente Paper Roll & Write, criado por portugueses.

Por isso não será a covid-19 a fazer desaparecer os novos jogos de tabuleiro. Quanto muito vai acelerar processos de adaptação. Vivendo nós na transformação da pós-digitalização, não restam dúvidas sobre o valor insubstituível da interacção humana presencial e da falta que nos faz. Por isso, assim que pudermos, voltaremos a jogar cara a cara, explorando as emoções de jogar com a humanidade diante de nós, à distância de um toque, adornada por sorrisos.”

+infos(fonte): LINK

Tags : ,

Programação avançada – capitulo 8 – maquina de estados, exemplo3 (revisitado)

Revisitar o exercício acerca do uso de maquina de estados com a implementação de um jogo de 3×3, onde existem dois jogadores.

A máquina de estados:
é um padrão de programação;
serve para abordar problemas mais complexos;
vai ajudar no fluxo na evolução das aplicações (redirecciona para o estado que vai fazer um determinado processamento);
permite identificar os momentos importantes, na evolução da aplicação/jogo;

Definição dos estados:
está relacionada com a interacção do utilizador;
existem transições entre os estados;
as transições de entrada (métodos) e podem/devem ser condicionados pelo estado do jogo;
as transições de saída (métodos) e podem/devem ser condicionados pelo estado do jogo;
existem então acções do utilizador sobre o jogo, e estas acções resultam em métodos, para haver evolução no jogo;
(identificar as etapas do jogo, que vão ser implementadas, para resolver mini problemas, sub dividir para se simplificar o código que é implementado);

Os estados, são assim:
são representados através de classes;
existe uma classe base, da qual os estados vão derivar (polimorfismo), normalmente do IEstado (através do implements);
e devem ser implementados todos os métodos abstractos que estão definidos no IEstado (usando return para o new “estado”) ou então fica nele próprio (return this).

(2)O IEstado, serve então:
é uma interface, dos estados (e não uma classe);
serve para representar/declarar o que existe, acções possíveis e comuns em todos os estados (através de métodos);
esses métodos são do tipo da interface IEstado;
mas atenção que podem existir algumas acções que podem não ser usadas/implementadas em todos os estados (fica o return this, nas bases), mas têm que ser aqui colocadas, construindo implementações default;
assim sendo os IEstado representam as evoluções (transições) do nosso modelo.

(3)O EstadoAdapter, serve então para:
é uma classe intermédia/genérica que vai implementar os comportamentos default, sem qualquer actualização de estado, para evitar a programação destas acções em estados onde não fazem sentido (normalmente ficam no mesmo estado).
só depois e em cada um dos estados é que os vamos implementar, pois só se justifica no contexto desse estado haver uma evolução na maquina de estados, e é em cada um desses estados que fica implementado
esta classe implementa o IEstado (implements)
implementa todos os métodos abstractos / default (return this), isto é, não existe evolução de estado/fica no mesmo estado.
surge aqui também os testes da evolução, assim estes estados vão ter que ter acesso aos dados do jogo (JogoDados), construtor e get e set
esta classe deve ser public abstract, para não ser instanciada,
e o JogoDados fica protected, evitando fazer uso do método getJogo, e pelas derivadas acedo a esta instância

(4)E agora construir os estados, que são classes derivadas e:
são extends EstadoAdapter
por ser protected a base, esta derivada tem que ter o construtor correspondente (super)
implementar os métodos correspondentes às acções que fazem sentido, são as setas de saída;
os outros não fazem sentido num estado, ficam com o default do EstadoAdapter
os métodos implementados em cada um dos estados fazem então um return new estado, ou this se voltar ao mesmo, sendo que é implementada também a lógica de cada um dos métodos

Surge uma classe
surge então o inicio da máquina de estados, representando numa classe que inicia o primeiro estado por onde arranca (new..), e que vai permitir aceder (get) ou alterar o estado (set)
esta classe vai ter que satisfazer as ordens do utilizador, define os métodos que permitem alterar/mudança de estado (métodos de transição)

Na iteração com o utilizador (texto ou gráfica):
recebe a maquina de estados;
obtém permanente o estado em que se encontra a máquina de estados;
faz uso de instanceof dos estados para invocar a interface que deve surgir ao utilizador.

neste exemplo:
(1)o JogoMaqEstados, gestão/ver a evolução/mudança entre os estados (no diagrama ir de um estado para outro), conhece o estado atual, é a classe de interface entre o IU do utilizador e todas as classes internas (quer aquelas que representam os estados, quer os dados, que representam o jogo (tem assim informação sobre o jogo (JogoDados), e o estado em que estamos (IEstado));
o JogoMaqEstados tem ainda a interface para todos os outros estados, onde actualiza a situação do estado;
os estados, recebem a entidade jogo, como referência;
já a grelha, onde estão as peças, o jogador, este tipo de dados ficam em JogoDados;
JogoDados, representa assim o tabuleiro do jogo, onde se fazem todas as verificações;
Jogador, representada cada um dos jogos;
Peca, as peças do tabuleiro de jogo;
IEstado;
EstadoAdapter

(10) A interface (IU) também precisa de ir buscar coisas ao jogo, mas quem trata disso é o JogoMaqEstados

package me_jogopecasr;

import me_jogopecasr.iu.texto.IUtexto;
import me_jogopecasr.logica.JogoMaqEstados;

public class Me_jogoPecasR {

    public static void main(String[] args) {

        IUtexto iuTexto = new IUtexto(new JogoMaqEstados());
        iuTexto.corre();
    }
}
package me_jogopecasr.iu.texto;

import java.util.Scanner;
import me_jogopecasr.logica.JogoMaqEstados;
import me_jogopecasr.logica.estados.AguardaColocacao;
import me_jogopecasr.logica.estados.AguardaDevolucao;
import me_jogopecasr.logica.estados.AguardaInicio;
import me_jogopecasr.logica.estados.IEstado;

public class IUtexto {

    private JogoMaqEstados jogo;
    private boolean sair = false;

    public IUtexto(JogoMaqEstados jogo) {
       this.jogo = jogo;
    }

    void iuAguardaInicio() {

        if ((jogo.getJogador1() != null && jogo.getJogador1().isGanhou())) {
            System.out.println("\n" + jogo.getJogador1() + "\n" + jogo.grelhaToString());
        } else if (jogo.getJogador2() != null && jogo.getJogador2().isGanhou()) {
            System.out.println("\n" + jogo.getJogador2() + "\n" + jogo.grelhaToString());
        }
        System.out.println("\n=== AGUARDA INICIO ===\n"
                + (jogo.getJogador1() != null ? "" + jogo.getJogador1() : "")
                + (jogo.getJogador2() != null ? "" + jogo.getJogador2() : ""));
        while (true) {
            System.out.println("\n0 - Sair\n1 - Define nome de jogador\n2 - Comecar jogo");
            char c = ' ';
            Scanner sc = new Scanner(System.in);
            c = sc.next().charAt(0);
            if ((c == '0')) {
                sair = true;
                return;
            }
            if ((c == '1')) { //1 - Define nome de jogador
                System.out.println("Numero (1 ou 2)  e nome do jogador: ");
                while (!sc.hasNextInt());
                int num = sc.nextInt();
                System.out.println(" numero: " + num);
                String nome = sc.next();
                jogo.defineNomeJogador(num, nome);
                System.out.println(" nome: " + nome);
                return;
            }
            if ((c == '2')) { //2 - Comecar jogo
                System.out.println("Comecar jogo: ");
                jogo.comecarJogo();
                return;
            }
        }
    }

    void iuAguardaColocacao() {
        System.out.println("\n=== AGUARDA COLOCACAO === \n"
                + jogo.getJogador1()
                + jogo.getJogador2()
                + "\nJogador activo: " + jogo.getNomeJogadorActivo()
                + "\n" + jogo.grelhaToString());

        System.out.println("\n1 - Jogar : linha  coluna\n2 - Abandonar");
        char c = ' ';
        Scanner sc = new Scanner(System.in);
        c = sc.next().charAt(0);
        if ((c == '1')) {

            System.out.print(jogo.getNomeJogadorActivo() + ">");

            while (!sc.hasNextInt());
            int linha = sc.nextInt();

            while (!sc.hasNextInt());
            int coluna = sc.nextInt();
            jogo.jogar(jogo.getNumJogadorActivo(), linha, coluna);
        }
        if ((c == '2')) {
            jogo.abandonar(jogo.getNumJogadorActivo());
            return;
        }
   
    }

    void iuAguardaDevolucao() {
        System.out.println("\n=== AGUARDA DEVOLUCAO === \n"
                + jogo.getJogador1()
                + jogo.getJogador2()
                + "\nJogador activo: " + jogo.getNomeJogadorActivo()
                + "\n" + jogo.grelhaToString());

//        System.out.println("\nDevolver : linha  coluna\nAbandonar: -1");
         System.out.println("\n1 - Devolver : linha  coluna\n2 - Abandonar");
        char c = ' ';
        Scanner sc = new Scanner(System.in);
        c = sc.next().charAt(0);
        if ((c == '1')) {
        System.out.print(jogo.getNomeJogadorActivo() + ">");
        while (!sc.hasNextInt());
        int linha = sc.nextInt();
 
        while (!sc.hasNextInt());
        int coluna = sc.nextInt();
        jogo.devolver(jogo.getNumJogadorActivo(), linha, coluna);
        }
        if ((c == '2')) {
            jogo.abandonar(jogo.getNumJogadorActivo());
            return;
        }
   
    }

    public void corre() {

        while (!sair) {
            IEstado estado = jogo.getEstado();
            if (estado instanceof AguardaInicio) {
                iuAguardaInicio();
            } else if (estado instanceof AguardaColocacao) {
                iuAguardaColocacao();
            } else if (estado instanceof AguardaDevolucao) {
                iuAguardaDevolucao();
            }
        }

    }
}
package me_jogopecasr.logica;

public interface Constantes {
    int DIM = 3;
}
package me_jogopecasr.logica;

import java.util.ArrayList;
import java.util.List;


public class Jogador implements Constantes {

    private JogoDados jogo;
    private String nome;
    private List<Peca> mao = new ArrayList<Peca>();
    private boolean ganhou;

    public Jogador(String nome, JogoDados j) {
        this.nome = nome;
        this.jogo = j;
        ganhou = false;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getNome() {
        return nome;
    }

    public boolean isGanhou() {
        return ganhou;
    }

    public void setGanhou(boolean ganhou) {
        this.ganhou = ganhou;
    }

    public void recebePecas() {
        mao.clear();
        for (int i = 0; i < DIM; i++) {
            mao.add(new Peca(this));
        }
        ganhou = false;
    }
     public void recebePeca(Peca peca) {
            mao.add(peca);   
    }

    public boolean temPecas() {
        return mao.size() > 0;
    }

    public boolean jogar(int linha, int coluna) {
        if (mao.size() == 0) {
            return false;
        }
        Peca peca = mao.get(0);
        if (jogo.setPeca(peca, linha, coluna)) {
            // jogou
            mao.remove(0);
            if (jogo.ganhou(this)) {
                ganhou = true;
            }
            return true;
        }
        return false;
    }

    @Override
    public String toString() {
        return "Jogador " + nome + " mao=" + mao + (ganhou ? " == GANHOU ==" : "") + "\n";
    }

    public int getNumPecas() {
        return mao.size();
    }
}
package me_jogopecasr.logica;

import java.util.ArrayList;
import java.util.List;

public class JogoDados implements Constantes{
 
    private List<Jogador> jogadores = new ArrayList<Jogador>();

    private int numJogActivo;
    private Peca[][] grelha;
    

    public JogoDados() {
        
        jogadores.add(new Jogador("A", this));
        jogadores.add(new Jogador("B", this));
        grelha = new Peca[DIM][DIM];
    }

    public Jogador getJogadorActivo() {
        return jogadores.get(numJogActivo - 1);
    }
    public String getNomeJogadorActivo() {
        return jogadores.get(numJogActivo - 1).getNome();
    }

    public Jogador getJogadorNaoActivo() {
        if (numJogActivo == 1) {
            return jogadores.get(1);
        } else if (numJogActivo == 2) {
            return jogadores.get(0);
        }
        return null;
    }

    public int getNumJogadorActivo() {
        return numJogActivo;
    }

    public int getNumJogadorNaoActivo() {
        return (numJogActivo == 1? 1: 2);
    }

    public Jogador getJogador1() {
        return jogadores.get(0);
    }

    public Jogador getJogador2() {
        return jogadores.get(1);
    }

    


    public Peca getPeca(int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return null;
        }
        if (grelha == null) {
            return null;
        }
        return grelha[linha][coluna];
    }

    public boolean setPeca(Peca peca, int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return false;
        }
        if (grelha[linha][coluna] != null) {
            return false;
        }
        grelha[linha][coluna] = peca;
        return true;
    }

    public boolean retiraPeca(int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return false;
        }
        if (grelha[linha][coluna] == null) {
            return false;
        }
        grelha[linha][coluna] = null;
        return true;
    }

    public void jogaOutro() {
        numJogActivo = numJogActivo == 1 ? 2 : 1;
    }

  
    public boolean inicializa() {

        grelha = new Peca[DIM][DIM];
        jogadores.get(0).recebePecas();
        jogadores.get(1).recebePecas();

        numJogActivo = 1;

        return true;
    }

    public boolean setNomeJogador(int num, String nome) {
        try{
            jogadores.get(num-1).setNome(nome);
            return true;
        }catch(IndexOutOfBoundsException e){
            return false;
        }
    }

    private boolean isEmDiagonalPrincipal(Jogador jogador) {
        for (int i = 0; i < DIM; i++) {
            if (grelha[i][i] == null || grelha[i][i].getJogador() != jogador) {
                return false;
            }
        }
        return true;
    }

    private boolean isEmDiagonalSecundaria(Jogador jogador) {
        for (int i = 0; i < DIM; i++) {
            if (grelha[i][DIM - 1 - i] == null || grelha[i][DIM - 1 - i].getJogador() != jogador) {
                return false;
            }
        }
        return true;
    }

    private boolean isEmDiagonal(Jogador jogador) {
        return isEmDiagonalPrincipal(jogador) || isEmDiagonalSecundaria(jogador);
    }

    private boolean isEmHorizontal(Jogador jogador, int linha) {
        for (Peca peca : grelha[linha]) {
            if (peca == null || peca.getJogador() != jogador) {
                return false;
            }
        }
        return true;
    }

    private boolean isEmVertical(Jogador jogador, int coluna) {
        for (int i = 0; i < grelha[coluna].length; i++) {
            Peca peca = grelha[i][coluna];
            if (peca == null || peca.getJogador() != jogador) {
                return false;
            }
        }
        return true;
    }

    private boolean isEmHorizontal(Jogador jogador) {
        for (int i = 0; i < DIM; i++) {
            if (isEmHorizontal(jogador, i)) {
                return true;
            }
        }
        return false;
    }

    private boolean isEmVertical(Jogador jogador) {
        for (int i = 0; i < DIM; i++) {
            if (isEmVertical(jogador, i)) {
                return true;
            }
        }
        return false;
    }

    public boolean ganhou(Jogador jogador) {

        return isEmHorizontal(jogador) || isEmDiagonal(jogador) || isEmVertical(jogador);
    }

      public boolean jogar(int numeroJogador, int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return false;
        }
        if(numJogActivo != numeroJogador){
            return false;
        }
      
        Jogador j = getJogadorActivo();
        
        
        if(!j.jogar(linha, coluna)){
            return false;
        }
        return true;
    }
    
      public boolean devolver(int numeroJogador, int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return false;
        }
        
        if(numJogActivo != numeroJogador){
            return false;
        }
       Peca peca = getPeca(linha, coluna);
        Jogador j = getJogadorActivo();
         if (peca == null || peca.getJogador() != j) {
            return false;
        }
        
        if(!retiraPeca(linha, coluna)){
            return false;
        }
        j.recebePeca(peca);
        return true;
    }
    
    public String grelhaToString() {
        String s = "";
        for (int i = 0; i < DIM; i++) {
            for (int j = 0; j < DIM; j++) {
                String casa;
                if (grelha[i][j] != null) {
                    casa = "" + grelha[i][j].getJogador().getNome().charAt(0);
                } else {
                    casa = " ";
                }
                s += "|\t" + casa + "\t";
            }
            s += "|\n";
        }
        return s;
    }

   
    public boolean acabou() {
        return ganhou(jogadores.get(0)) || ganhou(jogadores.get(1));

    }
    
}

package me_jogopecasr.logica;

import me_jogopecasr.logica.estados.AguardaInicio;
import me_jogopecasr.logica.estados.IEstado;

public class JogoMaqEstados {
    JogoDados jogo;
    IEstado estado;

    public JogoMaqEstados() {
        //1)
        this.jogo = new JogoDados();
        //7
        this.estado = new AguardaInicio(jogo);
    }

    public JogoDados getJogo() {
        return jogo;
    }

    public void setJogo(JogoDados jogo) {
        this.jogo = jogo;
    }

    public IEstado getEstado() {
        return estado;
    }

    public void setEstado(IEstado estado) {
        this.estado = estado;
    }

    //8 redirecionar, agulhar, as acções para os estados, o interface para todas as outras classes
    public void defineNomeJogador(int numeroJogador, String nome) {
        //8.1 a evolução do estado, retorna o estado seguinte
        estado = estado.defineNomeJogador(numeroJogador, nome); //ou usar o setEstado
    }

    public void comecarJogo() { //no infitivo
        estado = estado.comecaJogo();
    }

    public void jogar(int numeroJogador, int linha, int coluna) {
        estado = estado.joga(numeroJogador, linha, coluna);
    }

    public void devolver(int numeroJogador, int linha, int coluna) {
        estado = estado.devolve(numeroJogador, linha, coluna);
    }

    public void abandonar(int numeroJogador) {
        estado = estado.abandona(numeroJogador);
    }

    //9 redirencuionar para o modelo de dados
    public Jogador getJogador1() {
        return jogo.getJogador1();
    }

    public Jogador getJogador2() {
        return jogo.getJogador2();
    }

    //11 redirencuionar para o modelo de dados
    public String getNomeJogadorActivo() {
        return jogo.getNomeJogadorActivo();
    }

    public String grelhaToString() {
        return jogo.grelhaToString();
    }

    public int getNumJogadorActivo() {
        return jogo.getNumJogadorActivo();
    }

}

package me_jogopecasr.logica;

public class Peca {
    private Jogador jogador;

    public Peca(Jogador jogador) {
        this.jogador = jogador;
    }

    public Jogador getJogador() {
        return jogador;
    }

    public String toString(){
        return "" + jogador.getNome().charAt(0);
    }
}

package me_jogopecasr.logica.estados;

import me_jogopecasr.logica.JogoDados;
//5
public class AguardaColocacao extends EstadoAdapter{
    //6.2.3 ver se ele não colocou no mesmo local
    int l, c; 
    
    //5.1
    public AguardaColocacao(JogoDados jogo) {
        super(jogo);
        //6.2.5
        l = c-1;
    }
    //6.2.4
        public AguardaColocacao(JogoDados jogo, int l, int c) {
        super(jogo);
        this.l = l;
        this.c = c;
    }

    //5.2
    @Override
    public IEstado abandona(int numeroJogador) {
        //poderia ver qual é o jogador e atribuir a vitória ao outro
        return new AguardaInicio(jogo);
    }
    
    //5.3
    @Override
    public IEstado joga(int numeroJogador, int linha, int coluna) {
         //6.2.6, testar se é a mesma jogada
         if(linha == l && coluna == c){
             return this;
         }
        
        
        //5.3.1, vamos realizar o jogar
        if(!jogo.jogar(numeroJogador, linha, coluna)){
            return this;
        }
        
        //5.3.2, ver se ele ganhou
        if(jogo.getJogadorActivo().isGanhou()){
            return new AguardaInicio(jogo);
        }
        //5.3.3, mudar de jogador
        jogo.jogaOutro();
        
        //5.3.4, ver se tem peças 
         if(jogo.getJogadorActivo().isGanhou()){
            return new AguardaColocacao(jogo); //ou return this, fica no mesmo estado, e com l e c a -1
        }
         
         
         //5.3.5, 
         return new AguardaDevolucao(jogo);
    }
    
    
    
}


package me_jogopecasr.logica.estados;

import me_jogopecasr.logica.JogoDados;

//6
public class AguardaDevolucao extends EstadoAdapter {

    public AguardaDevolucao(JogoDados jogo) {
        super(jogo);
    }

    //6.1
    @Override
    public IEstado abandona(int numeroJogador) {
        return new AguardaInicio(jogo);
    }
    
    //6.2
    @Override
    public IEstado devolve(int numeroJogador, int linha, int coluna) {
        //6.2.1 adcionar a devolção
        if(jogo.devolver(numeroJogador, linha, coluna)){
            //o devolver já verifica
            return new AguardaColocacao(jogo, linha, coluna);  //6.2.7
        }
        //6.2.2 fica no mesmo estado
        return this;
    }
    
    
}

package me_jogopecasr.logica.estados;

import me_jogopecasr.logica.JogoDados;

public class AguardaInicio extends EstadoAdapter{
    //4) por ser protected a base, esta derivada tem que ter um construtor correspondente
    public AguardaInicio(JogoDados jogo) {
        super(jogo);
    }
    
    //4.2) implementar os métodos correspondentes às acções que fazem sentido, são as setas de saída

    @Override
    public IEstado defineNomeJogador(int numeroJogador, String nome) {
        //4.4
        jogo.setNomeJogador(numeroJogador, nome);
        //4.3
        return this;
    }

    @Override
    public IEstado comecaJogo() {
        //4.4 testes para começar um jogo
        if(jogo.getJogador1()== null || jogo.getJogador2() ==null){
        //fico no mesmo estado
        return this;
        }
        
        //12
        jogo.inicializa();
        return new AguardaColocacao(jogo);
    }
    
    
}

package me_jogopecasr.logica.estados;

    //3) existem acções que não fazem sent
import me_jogopecasr.logica.JogoDados;

public abstract class EstadoAdapter implements IEstado{
    //3.2) acesso aos jogos de dados
    //3.3) protected, evitando fazer uso do método getJogo, e pelas derivadas acedo a esta instancia
    protected JogoDados jogo;

    public EstadoAdapter(JogoDados jogo) {
        this.jogo = jogo;
    }

    public JogoDados getJogo() {
        return jogo;
    }

    public void setJogo(JogoDados jogo) {
        this.jogo = jogo;
    }
    
    
    //3.1)
    @Override
    public IEstado defineNomeJogador(int numeroJogador, String nome) {
        return this;
    }

    @Override
    public IEstado comecaJogo() {
        return this;
    }

    @Override
    public IEstado joga(int numeroJogador, int linha, int coluna) {
        return this;
    }

    @Override
    public IEstado devolve(int numeroJogador, int linha, int coluna) {
        return this;
    }

    @Override
    public IEstado abandona(int numeroJogador) {
        return this;
    }
    
}

package me_jogopecasr.logica.estados;

//2) definir todos os estados
public interface IEstado {
    IEstado defineNomeJogador(int numeroJogador, String nome);
    IEstado comecaJogo();
    IEstado joga(int numeroJogador, int linha, int coluna);
    IEstado devolve(int numeroJogador, int linha, int coluna);
    IEstado abandona(int numeroJogador);
}
Tags : , , ,

Humble RPG game development assets bundle: 2d art, music and sound effects (uma campanha)

Um conjunto de objetos para ajudar no desenvolvimento de videojogos do género RPG, da lista consta:
2D Characters Male
2D Hand Painted Mine Tileset
2D Hand Painted Snowland Tileset
58 Fantasy RPG Items
Souls RPG Graphics – Town Tileset
Souls RPG Graphics Tiles – Grasslands
Ancient Game SFX Pack
Clean City Game Assets
Cute RPG UI Kit
Dark RPG Chiptune Soundtrack Bundle
Dialogue Boxes
Dwarves vs Elves RPG Sprites
Elemental Magic Sound Effects Vol 1
Elemental Magic Sound Effects Vol 2
Fantasy Character Bundle
Fantasy RPG Items Vol 2
Farm & Fort Tileset & Icons
Forest Isometric Block Tileset
Frozen Village Isometric Block Tileset
Hand Painted Extra Objects Tileset
Human Fantasy Animated Pack
Interface SFX
Inventory Sounds Pack
Japanese Bar Interior Assets
Japanese City Game Assets
JRPG Character Pack
JRPG Music Pack
Lighthearted RPG Location Soundtrack Bundle
Lighthearted RPG Soundtrack Bundle
Medieval RPG UI Kit
MMORPG UI Kit
Monster Creature Animated Pack
Monster Creature Super Mix
Osaka City Game Assets
Pixel Art Beach Tile Set
Pixel Art Forest Road
Pixel Art Medieval Fantasy Characters
Pixel Art Medieval Interiors
Pixel Art Medieval UI Pack
Pixel Art Old Castle
Pixel Art Town
RPG Inventory – Fantasy Battles Axes
RPG Inventory – Fantasy Bows
RPG Inventory – Fantasy Daggers
RPG Inventory – Fantasy Potions
RPG Inventory – Fantasy Spears
RPG Inventory – Fantasy Swords
RPG Music Pack – Complete Collection
Side View Animated RPG Battlers
Souls RPG Graphics – Desert Tileset
Soul’s RPG Graphics – Sprites
Spells & Ability Icons
Survival Icons
Tyler Warren RPG Battlers Pixel Style
Warrior Adventure Game Characters

+infos(campanha): LINK

Tags : , , , , , ,

Humble Learning game coding and development bundle (uma campanha)

Está a decorrer uma campanha com referências a cursos para aprender a programar jogos e a desenvolver. Da campanha surge a lista de:
Applied Computer Vision with Unity and Azure
Battle Royale – Multiplayer Projects
Build a Micro-Strategy Game
Build an RPG Adventure in Phaser
C++ Programming for Beginners
Create a 2D RPG with Godot
Create Your First 3D Game with Unity
Develop a Puzzle Platformer Game
EasyAR and Marker-Based Apps for Beginners
Godot Game Development for Beginners
Humanoid Animation Tools for Beginners
Intro to Game Development with Unity
Intro to Multiplayer Game Development
Learn C++ by Making a Text-Based RPG
Player Authentication with Azure PlayFab
Python Programming by Making a Game
Real-time Strategy Project – Unit Movement
RPG – Multiplayer Projects
RPG Development with Phaser
The Complete Blender Course
The Complete Procedural Terrain Generation Course
Tile-Based Math Game Project
Turn-Based Game – Multiplayer Projects
Unity 2D Projects – Super Plumbers
Unity Cinemachine for Films and Games

+infos(a campanha): LINK

ps_ eu até achei interessante esta campanha por causa de alguns dos cursos que ele tem, contudo uma análise com mais critério apercebi-me que afinal estes cursos são cursos que fazem parte de um pack mais completo da malta da zenva.com nomeadamente do percurso Strategy Game Development Academy e que prefazem um total de 12 cursos online

Tags : , , , , ,

Oferta de trabalho

professores-adjuntos:
Instituição: Instituto Politécnico de Leiria – Escola Superior de Tecnologia e Gestão
Áreas: Jogos Digitais – Design Sonoro – LINK
Áreas: Jogos Digitais – Arte 2D – LINK
Áreas: Jogos Digitais – Ambientes Virtuais 3D – LINK

Tags :

Livros a ler/comprar.. prendas?

Eurogames: The Design, Culture and Play of Modern European Board Games, 2012, de Stewart Woods
+infos(na loja): LINK

It’s All a Game: The History of Board Games from Monopoly to Settlers of Catan, 2017,de Tristan Donovan
+infos(na loja): LINK

Game Design Workshop: A Playcentric Approach to Creating Innovative Games, Fourth Edition 4th Edition, 2018, de Tracy Fullerton
+infos(na loja): LINK

Rules of Play – Game Design Fundamentals, 2003, de K Salen
+infos(na loja): LINK

Tags : , ,

JavaFX, exercício 2

package javafx03;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx03.iu.gui.PaneOrganizer;

public class Javafx03 extends Application {

    public static void main(String[] args) {
        //redirecionar a execução para o método launch
        launch(args);
    }

    @Override
    public void init() throws Exception {
        //inicar qualquer coisa antes do start
        System.out.println("init");
        super.init();
    }

    //méotod obrigatório abstract do Application
    @Override
    public void start(Stage stage) throws Exception {
        System.out.println("start");
        //no palco vão ocorrer cenas
        PaneOrganizer po = new PaneOrganizer();
        Scene cena = new Scene(po, 600, 400);
        stage.setScene(cena);
        stage.setTitle("programação avançada!");
        stage.setMinWidth(300);
        stage.setMinHeight(200);
        stage.show();
    }

    @Override
    public void stop() throws Exception {
        System.out.println("stop");
        super.stop();
    }

}
package javafx03.iu.gui;

import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;

import javafx.scene.shape.Ellipse;
import javafx03.logica.Figura;

//pane, usa o espaço todo disponivel
public class PaneOrganizer extends Pane {

    //criar a figura
    Figura figura;
    Ellipse elipse;

    public PaneOrganizer() {
        this.figura = new Figura();
        figura.setPrimeiroPonto(0, 0);
        figura.setSegundoPonto(100, 50);
        figura.setRGB(0, 255, 0);

        criarLayout();
        registaListeners();
        atualizaFigura();
    }

    private void criarLayout() {
        //criar uma nova elipse
        /*
        elipse.setCenterX(200);
        elipse.setCenterY(100);
        elipse.setFill(Color.GREEN);
        elipse.setRadiusX(200); 
        elipse.setRadiusY(100);
         */
        //adicionar a elipse ao pane
        elipse = new Ellipse();
        this.getChildren().add(elipse);
    }

    private void registaListeners() {
        //podia ser feito numa classe à parte
        this.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent t) {
                figura.setPrimeiroPonto((int) t.getX(), (int) t.getY());
                figura.setSegundoPonto((int) t.getX(), (int) t.getY());
                figura.setRGB((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256));
                atualizaFigura();
            }
        });
        //com lambda
        this.setOnMouseDragged((t) -> {
            figura.setSegundoPonto((int) t.getX(), (int) t.getY());
            atualizaFigura();
        });

//        this.setOnMouseReleased((t) -> {
//            figura.setSegundoPonto((int) t.getX(), (int) t.getY());
//            atualizaFigura();
//        });

    }

    private void atualizaFigura() {
        elipse.setFill(Color.rgb(figura.getR(), figura.getG(), figura.getB()));
        elipse.setCenterX((figura.getXi() + figura.getXf()) / 2);
        elipse.setCenterY((figura.getYi() + figura.getYf()) / 2);
        elipse.setRadiusX(figura.getAltura() / 2);
        elipse.setRadiusY(figura.getLargura() / 2);
    }
}
package javafx03.logica;

public class Figura {

    int xi, xf;
    int yi, yf;
    int r, g, b;

    public Figura() {

    }

    public int getXi() {
        return xi;
    }

    public int getXf() {
        return xf;
    }

    public int getYi() {
        return yi;
    }

    public int getYf() {
        return yf;
    }

    public int getR() {
        return r;
    }

    public int getG() {
        return g;
    }

    public int getB() {
        return b;
    }

    public void setRGB(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    public void setPrimeiroPonto(int x, int y) {
        this.xi = x;
        this.yi = y;
    }

    public void setSegundoPonto(int x, int y) {
        this.xf = x;
        this.yf = y;
    }

    public int getLargura() {
        return Math.abs(xf - xi);
    }

    public int getAltura() {
        return Math.abs(xf - xi);
    }
}
Tags : ,

JavaFX, exercício 1

Uso de:

Com:

package javafx02;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx02.PaneOrganizer.PaneOrganizer;

public class JavaFx02 extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void init() throws Exception {
        super.init();
        System.out.println("onInit");
    }

    @Override
    public void start(Stage stage) throws Exception {
        System.out.println("onStart");
        PaneOrganizer brd = new PaneOrganizer();
         Scene scene = new Scene(brd, 600, 400);
        stage.setScene(scene);
        stage.setTitle("Programação avançada");
        stage.show();
    }

    @Override
    public void stop() throws Exception {
        super.stop();
        System.out.println("onStop");
    }
}
package javafx02.PaneOrganizer;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;

public class PaneOrganizer extends BorderPane {

    Button btnVerde, btnAzul;
    Label lblVerde, lblAzul;
    int numerVerde, numeroAzul;

    public PaneOrganizer() {
        createPane();
        //registerListener1();
        //registerListener2();
        //registerListener3();
        //registerListener4();
        registerListener5();
        numerVerde=0;
        numeroAzul=0;
    }

    void createPane() {
        btnVerde = new Button("Green");
        btnVerde.setTextFill(Color.GREEN);
        btnAzul = new Button("Blue");
        btnAzul.setTextFill(Color.BLUE);
        //numeros dinamicos
        lblVerde = new Label("#Verde: 0");
        lblAzul = new Label("#Azul: 0");

        HBox hbox = new HBox();
        hbox.getChildren().addAll(btnVerde, btnAzul);
        hbox.setSpacing(25);
        hbox.setPadding(new Insets(10));
        hbox.setAlignment(Pos.CENTER);
        this.setTop(hbox);
        
        //numeros dinamicos
         HBox hboxlbl = new HBox();
        hboxlbl.getChildren().addAll(lblVerde, lblAzul);
        hboxlbl.setSpacing(25);
        hboxlbl.setPadding(new Insets(10));
        hboxlbl.setAlignment(Pos.CENTER);
        hboxlbl.setBackground(new Background(new BackgroundFill(Color.rgb(255, 255, 255), CornerRadii.EMPTY, Insets.EMPTY)));
        this.setBottom(hboxlbl);

        /*
        //outras coisas!
        Label l1 = new Label("Texto 1");
        l1.setFont(Font.font("Times New Roman", FontWeight.BOLD, FontPosture.ITALIC, 24));
        l1.setTextFill(Color.rgb(0, 255, 0));
        l1.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
        l1.setPadding(new Insets(10, 20, 30, 40));
        //brd.setTop(l1);

        Label l2 = new Label("Texto 2");
        l2.setTextFill(Color.rgb(0, 123, 123));

        HBox hbox = new HBox();
        hbox.getChildren().addAll(l1, l2);
        hbox.setStyle("-fx-background-color: #00ffff;"); //outra forma, css
        hbox.setPadding(new Insets(10));
        hbox.setAlignment(Pos.CENTER);
        hbox.setSpacing(25);
        this.setTop(hbox);

        //brd.setBottom(l2);
        BorderPane.setAlignment(l2, Pos.CENTER);
         */
    }

    //1ºalternativa, usando uma inner class
    void registerListener1() {
        btnVerde.setOnAction(new ProgBotaoVerde1()); //o que vai ser executado, um objecto do tipo!
        btnAzul.setOnAction(new ProgBotaoAzul1()); //o que vai ser executado, um objecto do tipo!
    }

    class ProgBotaoVerde1 implements EventHandler<ActionEvent> {

        @Override
        public void handle(ActionEvent t) {
            //quando o botão for clicado
            System.out.println("Oi botão verde - inner class");
            //aceder ao objecto de fora ou então criar uma varivel ou via construtor
            PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 255, 0), CornerRadii.EMPTY, Insets.EMPTY)));
        }

    }

    class ProgBotaoAzul1 implements EventHandler<ActionEvent> {

        @Override
        public void handle(ActionEvent t) {
            //quando o botão for clicado
            System.out.println("Oi botão azul - inner class");
            //aceder ao objecto de fora ou então criar uma varivel ou via construtor
            PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 255), CornerRadii.EMPTY, Insets.EMPTY)));
        }
    }

    //2ºalternativa, usando uma classe inline
    void registerListener2() {
        btnVerde.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                System.out.println("Oi botão azul - inline");
                PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 255, 0), CornerRadii.EMPTY, Insets.EMPTY)));
            }
        });
        btnAzul.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                System.out.println("Oi botão azul - inline");
                PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 255), CornerRadii.EMPTY, Insets.EMPTY)));
            }
        });
    }

    //3ºalternativa, usando uma classe inline
    void registerListener3() {
        btnVerde.setOnAction(new ProcessaBotao(Color.GREEN)); //o que vai ser executado, um objecto do tipo!
        btnAzul.setOnAction(new ProcessaBotao(Color.BLUE)); //o que vai ser executado, um objecto do tipo!
    }

    class ProcessaBotao implements EventHandler<ActionEvent> {
        Color cor;
        public ProcessaBotao(Color cor) {
            this.cor = cor;
        }
        @Override
        public void handle(ActionEvent t) {
            //quando o botão for clicado
            System.out.println("Oi botão azul - inner class");
            //aceder ao objecto de fora ou então criar uma varivel ou via construtor
            PaneOrganizer.this.setBackground(new Background(new BackgroundFill(cor, CornerRadii.EMPTY, Insets.EMPTY)));
        }
    }
    
    //4ºalternativa, usando lambda functions
    void registerListener4() {
    btnVerde.setOnAction(
            (t)->{
                PaneOrganizer.this.setBackground(
                        new Background(new BackgroundFill(Color.GREEN, CornerRadii.EMPTY, Insets.EMPTY)));
            });
   
    btnAzul.setOnAction(
            (t)->{
                PaneOrganizer.this.setBackground(
                        new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
            });
    }
    
    //dinamicos
    void registerListener5() {
        btnVerde.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                System.out.println("Oi botão azul - inline");
                PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 255, 0), CornerRadii.EMPTY, Insets.EMPTY)));
                numerVerde++;
                lblVerde.setText("#Verde: "+numerVerde);
            }
        });
        btnAzul.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                System.out.println("Oi botão azul - inline");
                PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 255), CornerRadii.EMPTY, Insets.EMPTY)));
                numeroAzul++;
                lblAzul.setText("#Azul: "+numeroAzul);
            }
        });
    }
}
Tags :

Programação avançada – capitulo 8 – maquina de estados, exemplo3

Exemplo de uma maquina de estado, usando como referência a implementação de um jogo de 3×3, onde existem dois jogadores.
O diagrama da maquina de estados dado:

Outra versão do diagrama da maquina de estados:

Em que ficou:

Com:

package me_jogopecas;

import me_jogopecas.iu.texto.IUTexto;
import me_jogopecas.logica.estados.JogoMaquinaEstados;

public class Me_jogoPecas {

    public static void main(String[] args) {
           IUTexto iuTexto = new IUTexto(new JogoMaquinaEstados());
           iuTexto.corre();
    }
    
}
package me_jogopecas.iu.texto;

import java.util.Scanner;
import me_jogopecas.logica.estados.*;

public class IUTexto {

    private JogoMaquinaEstados jogo;
    private boolean sair = false;

    public IUTexto(JogoMaquinaEstados jogo) {
        this.jogo = jogo;
    }

    void iuAguardaInicio() {

        if ((jogo.getJogador1() != null && jogo.getJogador1().isGanhou())) {
            System.out.println("\n" + jogo.getJogador1() + "\n" + jogo.grelhaToString());
        } else if (jogo.getJogador2() != null && jogo.getJogador2().isGanhou()) {
            System.out.println("\n" + jogo.getJogador2() + "\n" + jogo.grelhaToString());
        }
        System.out.println("\n=== AGUARDA INICIO ===\n"
                + (jogo.getJogador1() != null ? "" + jogo.getJogador1() : "")
                + (jogo.getJogador2() != null ? "" + jogo.getJogador2() : ""));
        while (true) {
            System.out.println("\n0 - Sair\n1 - Define nome de jogador\n2 - Comecar jogo\n3 - Ler jogo");
            char c = ' ';
            Scanner sc = new Scanner(System.in);
            c = sc.next().charAt(0);
            if ((c == '0')) {
                sair = true;
                return;
            }
            if ((c == '1')) { //1 - Define nome de jogador
                System.out.println("Numero (1 ou 2)  e nome do jogador: ");
                while (!sc.hasNextInt());
                int num = sc.nextInt();
                System.out.println(" numero: " + num);
                String nome = sc.next();
                jogo.defineNomeJogador(num, nome);
                System.out.println(" nome: " + nome);
                return;
            }
            if ((c == '2')) { //2 - Comecar jogo
                System.out.println("Comecar jogo: ");
                jogo.comecarJogo();
                return;
            }
            if ((c == '3')) { //3 - Ler o jogo
                System.out.println("Ler o  jogo: ");
                String ficheiroLer = sc.next();
               // jogo.ler(ficheiroLer);
                return;
            }
        }
    }

    void iuAguardaColocacao() {
        System.out.println("\n=== AGUARDA COLOCACAO === \n"
                + jogo.getJogador1()
                + jogo.getJogador2()
                + "\nJogador activo: " + jogo.getNomeJogadorActivo()
                + "\n" + jogo.grelhaToString());

        System.out.println("\n1 - Jogar : linha  coluna\n2 - Abandonar");
        char c = ' ';
        Scanner sc = new Scanner(System.in);
        c = sc.next().charAt(0);
        if ((c == '1')) {

            System.out.print(jogo.getNomeJogadorActivo() + ">");

            while (!sc.hasNextInt());
            int linha = sc.nextInt();

            while (!sc.hasNextInt());
            int coluna = sc.nextInt();
            jogo.jogar(jogo.getNumJogadorActivo(), linha, coluna);
        }
        if ((c == '2')) {
            System.out.println("Guardar o  jogo: ");
            String ficheiroGuardar = sc.next();
           // jogo.grava(ficheiroGuardar);
            jogo.abandonar(jogo.getNumJogadorActivo());
            return;
        }
    }

    void iuAguardaDevolucao() {
        System.out.println("\n=== AGUARDA DEVOLUCAO === \n"
                + jogo.getJogador1()
                + jogo.getJogador2()
                + "\nJogador activo: " + jogo.getNomeJogadorActivo()
                + "\n" + jogo.grelhaToString());

//        System.out.println("\nDevolver : linha  coluna\nAbandonar: -1");
        System.out.println("\n1 - Devolver : linha  coluna\n2 - Abandonar");
        char c = ' ';
        Scanner sc = new Scanner(System.in);
        c = sc.next().charAt(0);
        if ((c == '1')) {
            System.out.print(jogo.getNomeJogadorActivo() + ">");
            while (!sc.hasNextInt());
            int linha = sc.nextInt();

            while (!sc.hasNextInt());
            int coluna = sc.nextInt();
            jogo.devolver(jogo.getNumJogadorActivo(), linha, coluna);
        }
        if ((c == '2')) {
            jogo.abandonar(jogo.getNumJogadorActivo());
            return;
        }
    }

    public void corre() {

        while (!sair) {
            IEstado estado = jogo.getEstado();
            if (estado instanceof AguardaInicio) {
                iuAguardaInicio();
            } else if (estado instanceof AguardaColocacao) {
                iuAguardaColocacao();
            } else if (estado instanceof AguardaDevolucao) {
                iuAguardaDevolucao();
            }
        }

    }
}
package me_jogopecas.logica;

public interface Constantes {
    int DIM  =3;
}
package me_jogopecas.logica;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static me_jogopecas.logica.Constantes.*;

public class Jogador implements Constantes, Serializable{

    private String nome;
    private JogoDados jogo;
    
    //lista de peças no inicio
    private List<Peca> mao = new ArrayList<Peca>(); 
    
    private boolean ganhou;

    public Jogador(String nome, JogoDados j) {
        this.nome = nome;
        this.jogo = j;
        ganhou = false;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public JogoDados getJogo() {
        return jogo;
    }

    public void setJogo(JogoDados jogo) {
        this.jogo = jogo;
    }

    public List<Peca> getMao() {
        return mao;
    }

    public void setMao(List<Peca> mao) {
        this.mao = mao;
    }

    public boolean isGanhou() {
        return ganhou;
    }

    public void setGanhou(boolean ganhou) {
        this.ganhou = ganhou;
    }

    //quando começar o jogo!
    public void recebePecas() {
        mao.clear();
        for (int i = 0; i < DIM; i++) { mao.add(new Peca(this)); } ganhou = false; } public int getNumPecas(){ return mao.size(); } //quando a peça é devolvida public void recebePeca(Peca peca) { mao.add(new Peca(this)); } public boolean temPecas() { return mao.size() > 0;
    }
    
    public boolean jogar(int linha, int coluna){
        if(mao.size()==0){
            return false;
        }
        //as peças são todas iguais
        Peca peca = mao.get(0);
        if(jogo.setPeca(peca, linha, coluna)){
            //jogou
            mao.remove(0);
            if(jogo.ganhou(this)){
                ganhou = true;
            }
            return true;
        }
        return false;
    }

    @Override
    public String toString() {
        return "Jogador " + nome + " mao=" + mao + (ganhou ? " == GANHOU ==" : "") + "\n" ;
    }   
}
package me_jogopecas.logica;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class JogoDados implements Constantes, Serializable  {

    private List<Jogador> jogadores = new ArrayList<Jogador>();

    private int numJoActivo;
    private Peca[][] grelha;

    public JogoDados() {
        jogadores.add(new Jogador("A", this));
        jogadores.add(new Jogador("B", this));
        grelha = new Peca[DIM][DIM];
    }

    public Jogador getJogadorActivo() {
        //considera-se que o jogador activo é 1 ou 2, daí o -1
        return jogadores.get(numJoActivo - 1);
    }

    public String getNomeJogadorActivo() {
        return jogadores.get(numJoActivo - 1).getNome();
    }

    public Jogador getJogadorNaoActivo() {
        if (numJoActivo == 1) {
            return jogadores.get(1);
        } else if (numJoActivo == 2) {
            return jogadores.get(0);
        }
        return null;
    }

    public int getNumJogadorActivo() {
        return numJoActivo;
    }

    public int getNumJogadorNaoActivo() {
        return (numJoActivo == 1 ? 1 : 2);
    }

    public Jogador getJogador1() {
        return jogadores.get(0);
    }

    public Jogador getJogador2() {
        return jogadores.get(1);
    }

    public Peca getPeca(int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return null;
        }
        if (grelha == null) {
            return null;
        }
        return grelha[linha][coluna];
    }

    public boolean setPeca(Peca peca, int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return false;
        }
        if (grelha[linha][coluna] != null) {
            return false;
        }
        grelha[linha][coluna] = peca;
        return true;
    }

    public boolean retiraPeca(int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return false;
        }
        if (grelha[linha][coluna] == null) {
            return false;
        }
        grelha[linha][coluna] = null;
        return true;
    }

    public void jogaOutro() {
        numJoActivo = numJoActivo == 1 ? 2 : 1;
    }

    public boolean inicializa() {

        grelha = new Peca[DIM][DIM];
        jogadores.get(0).recebePecas();
        jogadores.get(1).recebePecas();

        numJoActivo = 1;

        return true;
    }

    public boolean setNomeJogador(int num, String nome) {
        //falta validar se o num é válido
        try {
            //tento aceder a um elemento do array que nao existe
            jogadores.get(num - 1).setNome(nome);
            return true;
        } catch (IndexOutOfBoundsException e) {
            return false;
        }
    }

    private boolean isEmDiagonalPrincipal(Jogador jogador) {
        for (int i = 0; i < DIM; i++) {
            if (grelha[i][i] == null || grelha[i][i].getJogador() != jogador) {
                return false;
            }
        }
        return true;
    }

    private boolean isEmDiagonalSecundaria(Jogador jogador) {
        for (int i = 0; i < DIM; i++) {
            if (grelha[i][DIM - 1 - i] == null || grelha[i][DIM - 1 - i].getJogador() != jogador) {
                return false;
            }
        }
        return true;
    }

    private boolean isEmDiagonal(Jogador jogador) {
        return isEmDiagonalPrincipal(jogador) || isEmDiagonalSecundaria(jogador);
    }

    private boolean isEmHorizontal(Jogador jogador, int linha) {
        for (Peca peca : grelha[linha]) {
            if (peca == null || peca.getJogador() != jogador) {
                return false;
            }
        }
        return true;
    }

    private boolean isEmVertical(Jogador jogador, int coluna) {
        for (int i = 0; i < grelha[coluna].length; i++) {
            Peca peca = grelha[i][coluna];
            if (peca == null || peca.getJogador() != jogador) {
                return false;
            }
        }
        return true;
    }

    private boolean isEmHorizontal(Jogador jogador) {
        for (int i = 0; i < DIM; i++) {
            if (isEmHorizontal(jogador, i)) {
                return true;
            }
        }
        return false;
    }

    private boolean isEmVertical(Jogador jogador) {
        for (int i = 0; i < DIM; i++) {
            if (isEmVertical(jogador, i)) {
                return true;
            }
        }
        return false;
    }

    public boolean ganhou(Jogador jogador) {
        return isEmHorizontal(jogador) || isEmDiagonal(jogador) || isEmVertical(jogador);
    }

    public boolean jogar(int numeroJogador, int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return false;
        }
        if (numJoActivo != numeroJogador) {
            return false;
        }

        Jogador j = getJogadorActivo();

        if (!j.jogar(linha, coluna)) {
            return false;
        }
        return true;
    }

    public boolean devolver(int numeroJogador, int linha, int coluna) {
        if (linha < 0 || linha >= DIM || coluna < 0 || coluna >= DIM) {
            return false;
        }

        if (numJoActivo != numeroJogador) {
            return false;
        }
        Peca peca = getPeca(linha, coluna);
        Jogador j = getJogadorActivo();
        if (peca == null || peca.getJogador() != j) {
            return false;
        }

        if (!retiraPeca(linha, coluna)) {
            return false;
        }
        j.recebePeca(peca);
        return true;
    }

    public String grelhaToString() {
        String s = "";
        for (int i = 0; i < DIM; i++) {
            for (int j = 0; j < DIM; j++) {
                String casa;
                if (grelha[i][j] != null) {
                    casa = "" + grelha[i][j].getJogador().getNome().charAt(0);
                } else {
                    casa = " ";
                }
                s += "|\t" + casa + "\t";
            }
            s += "|\n";
        }
        return s;
    }

    public boolean acabou() {
        return ganhou(jogadores.get(0)) || ganhou(jogadores.get(1));
    }
}
package me_jogopecas.logica;

import java.io.Serializable;

public class Peca implements Serializable {
    private Jogador jogador;

    public Peca(Jogador jgd) {
        this.jogador = jgd;
    }

    public Jogador getJogador() {
        return jogador;
    }

    public void setJogador(Jogador jogador) {
        this.jogador = jogador;
    }

    @Override
    public String toString() {
        return "" + jogador.getNome().charAt(0);
    }
}
package me_jogopecas.logica.estados;

import me_jogopecas.logica.JogoDados;

public class AguardaColocacao extends EstadoAdapter {

    //para informação adicional
    int l, c;

    public AguardaColocacao(JogoDados jogodados) {
        super(jogodados);
        //para informação adicional
        l = c = -1; //se vier return this
    }

    //para informação adicional
    public AguardaColocacao(JogoDados jogo, int l, int c) {
        super(jogo);
        this.l = l;
        this.c = c;
    }

    //setas de saida
    @Override
    public IEstado abandona(int numeroJogador) {
        //podiamos atribuir a vitoria a um jogador, olhando para numeroJogador

        //voltamos ao inicio
        return new AguardaInicio(jogodados);
    }

    @Override
    public IEstado joga(int numeroJogador, int linha, int coluna) {
        //informação adicional
        if (linha == l && coluna == c) {
            return this;
        }

        //jogar normal, o jogar faz o teste se pode ou nao jogar
        jogodados.jogar(numeroJogador, linha, coluna);
        //verificar se o jogador activo
        if (jogodados.getJogadorActivo().isGanhou()) {
            return new AguardaInicio(jogodados);
        }
        jogodados.jogaOutro();
        //jogar a um jogador que não tem peças
        //jogar que nao tem pecas
        if (jogodados.getJogadorActivo().temPecas()) {
            //return this; //tb funciona
            //para informação adicional
            return new AguardaColocacao(jogodados);
        }
        return new AguardaDevolucao(jogodados);
    }
}
package me_jogopecas.logica.estados;

import me_jogopecas.logica.JogoDados;

public class AguardaDevolucao extends EstadoAdapter {

    public AguardaDevolucao(JogoDados jogodados) {
        super(jogodados);
    }

    @Override
    public IEstado abandona(int numeroJogador) {
        return new AguardaInicio(jogodados);
    }

    @Override
    public IEstado devolve(int numeroJogador, int linha, int coluna) {
        if (jogodados.devolver(numeroJogador, linha, coluna)) {
            //return new AguardaColocacao(jogodados);
            //informação adicional
            return new AguardaColocacao(jogodados, linha, coluna);
        }
        return this;
    }
}
package me_jogopecas.logica.estados;

import me_jogopecas.logica.JogoDados;

public class AguardaInicio extends EstadoAdapter {

    //construtor que redireciona para a classe base
    public AguardaInicio(JogoDados jogodados) {
        super(jogodados);
    }

    //setas de saida, os outros não fazem sentido
    @Override
    public IEstado defineNomeJogador(int numeroJogador, String nome) {
        //qual o novo estado depois de defineNomeJogador
        //agora vou atuar sobre o jogo
        jogodados.setNomeJogador(numeroJogador, nome);

        return this; //fico no mesmo estado
    }

    @Override
    public IEstado comecaJogo() {
        //testes para verificar se já tinhamos os dois jogadores
        if(jogodados.getJogador1()==null || jogodados.getJogador2()==null){
            return this;
        }
        jogodados.inicializa();
        return new AguardaColocacao(jogodados);
    }

}

package me_jogopecas.logica.estados;

import java.io.Serializable;
import me_jogopecas.logica.JogoDados;

//abstract para nao ser instanciada 
public abstract class EstadoAdapter implements IEstado, Serializable{
    
    
    JogoDados jogodados;//tem que ter acesso aos dados de jogo

    //podemos colocar como protected, para ser usado apenas nas classes derivadas
    public EstadoAdapter(JogoDados jogod) {
        this.jogodados = jogod;
    }

    public JogoDados getJogodados() {
        return jogodados;
    }

    public void setJogodados(JogoDados jogodados) {
        this.jogodados = jogodados;
    }

    @Override
    public IEstado defineNomeJogador(int numeroJogador, String nome) {
       return this;
    }

    @Override
    public IEstado comecaJogo() {
       return this;
    }

    @Override
    public IEstado joga(int numeroJogador, int linha, int coluna) {
        return this;
    }

    @Override
    public IEstado devolve(int numeroJogador, int linha, int coluna) {
        return this;
    }

    @Override
    public IEstado abandona(int numeroJogador) {
        return this;
    }
    //o interface precisa de ir buscar coisas para as mostrar
    
}

package me_jogopecas.logica.estados;

public interface IEstado {
    //definir todas as acções o jogo
    IEstado defineNomeJogador(int numeroJogador, String nome);
    IEstado comecaJogo();
    IEstado joga(int numeroJogador, int linha, int coluna);
    IEstado devolve(int numeroJogador, int linha, int coluna);
    IEstado abandona(int numeroJogador);
}

package me_jogopecas.logica.estados;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import me_jogopecas.logica.Jogador;
import me_jogopecas.logica.JogoDados;

public class JogoMaquinaEstados implements Serializable {

    //se JogoMaquinaEstados é Serializable as classes de base tb têm que ser
    JogoDados jogodados;
    IEstado estado;

    public JogoMaquinaEstados() {
        //criar o meu jogo
        this.jogodados = new JogoDados();
        //desenvolver, evoluir, programação dos estados (do diagrama)
        this.estado = new AguardaInicio(jogodados);
    }

    public IEstado getEstado() {
        return estado;
    }

    public void setEstado(IEstado estado) {
        this.estado = estado;
    }

    //implementar todos os estados, através desta maquina de estados
    //interface de todas as classes
    public void defineNomeJogador(int numeroJogador, String nome) {
        //estado seguinte vai ser do retornar..
        estado = estado.defineNomeJogador(numeroJogador, nome);
    }

    public void comecarJogo() {
        estado = estado.comecaJogo();
    }

    public void jogar(int numeroJogador, int linha, int coluna) {
        estado = estado.joga(numeroJogador, linha, coluna);
    }

    public void devolver(int numeroJogador, int linha, int coluna) {
        estado = estado.devolve(numeroJogador, linha, coluna);
    }

    public void abandonar(int numeroJogador) {
        estado = estado.abandona(numeroJogador);
    }

    //os métodos que surgem no IUTexto
    public Jogador getJogador1() {
        return jogodados.getJogador1();
    }

    public Jogador getJogador2() {
        return jogodados.getJogador2();
    }

    public String getNomeJogadorActivo() {
        return jogodados.getNomeJogadorActivo();
    }

    public int getNumJogadorActivo() {
        return jogodados.getNumJogadorActivo();
    }

    public String grelhaToString() {
        return jogodados.grelhaToString();
    }

    //ou em alternativa dava acesso ao jogodados
    public JogoDados getJogo() {
        return jogodados;
    }

    //gravação dos ficheiros
    public void grava(String filename) throws IOException {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(filename));
            oos.writeObject(this);
        } finally {
            if(oos != null)
            {
                oos.close();
            }
        }
    }
    public static JogoMaquinaEstados ler(String filename) throws IOException, ClassNotFoundException {
    ObjectInputStream oos = null;
        try {
            oos = new ObjectInputStream(new FileInputStream(filename));
            return (JogoMaquinaEstados) oos.readObject();
        } finally {
            if(oos != null)
            {
                oos.close();
            }
        }
    
    }
}

Tags : , ,

Programação avançada – capitulo 8 – maquina de estados, exemplo2

Exemplo de uma maquina de estado, simples, usando as referências a um mecanismo de um elevador com dois estados extra: erro e chaveManutencao

Em que ficou:

Com:

package me_me_elevador;

import me_me_elevador.IU.Texto.UsaElevador;
import me_me_elevador.logica.Elevador;

public class Me_ME_elevador {
    public static void main(String[] args) {
        Elevador e = new Elevador();
        UsaElevador usaE = new UsaElevador(e); //interface com o utilizador
        usaE.corre();
    }
}
package me_me_elevador.IU.Texto;

import me_me_elevador.logica.*;
import me_me_elevador.util.IUUtil;

public class UsaElevador {

    //o interface com o utilizador, com o nosso elevador.
    private Elevador elevador;
    private boolean sair = false;

    public UsaElevador(Elevador elevador) {
        this.elevador = elevador;
    }

    public void corre() {
        while (!sair) {
            //saber em que estado estou
            IEstado estado = elevador.getEstado();
            if (estado instanceof RC) {
                iuRC();
            } else if (estado instanceof Andar1) {
                iuAndar1();
            } else if (estado instanceof Andar2) {
                iuAndar2();
            } else if (estado instanceof Andar3) {
                iuAndar3();
            }else if (estado instanceof Manutencao) {
                iuManutencao();
            }
        }
    }
    
    
     private void iuManutencao() {
        int opcao = IUUtil.getOpcao("Em modo de manutenção", "Usar a chave", "Sair do elevador");
        switch (opcao) {
            case 0:
                elevador.chaveManutencao();
            case 1:
                sair = true;
                break;
        }
    }

    private void iuRC() {
        //opcoa 1
        //pedir ao utilizador: sair ou subir
        int opcao = IUUtil.getOpcao("Piso R/C", "Sair do prédio", "Subir", "Erro");
        switch (opcao) {
            case 0:
                sair = true;
                break;
            case 1:
                //fazer evoluir a maquina de estados
                elevador.sobe();
                break;
            case 2:
                elevador.erro();
                break;
        }
    }

    private void iuAndar1() {
        int opcao = IUUtil.getOpcao("Piso 1 andar", "Descer", "Subir", "Erro");
        switch (opcao) {
            case 0:
                //fazer evoluir a maquina de estados
                elevador.desce();
                break;
            case 1:
                //fazer evoluir a maquina de estados
                elevador.sobe();
                break;
            case 2:
                elevador.erro();
                break;
        }
    }

    private void iuAndar2() {
        int opcao = IUUtil.getOpcao("Piso 2 andar", "Descer", "Subir", "Erro");
        switch (opcao) {
            case 0:
                //fazer evoluir a maquina de estados
                elevador.desce();
                break;
            case 1:
                //fazer evoluir a maquina de estados
                elevador.sobe();
                break;
            case 2:
                elevador.erro();
                break;
        }
    }

    private void iuAndar3() {
        int opcao = IUUtil.getOpcao("Piso 3 andar", "Descer", "Erro");
        switch (opcao) {
            case 0:
                //fazer evoluir a maquina de estados
                elevador.desce();
                break;
            case 1:
                elevador.erro();
                break;
        }
    }
}
package me_me_elevador.logica;

public class Andar1 extends Piso {

    @Override
    public IEstado sobe() {
        //altero de estado
        return new Andar2();
    }

    @Override
    public IEstado desce() {
        //altero de estado
        return new RC();
    }
    
    //se fossem todos iguais podia ser implementado no Piso
    // com return this;
    @Override
    public IEstado erro() {
        return new Manutencao(this);
    }
}
package me_me_elevador.logica;

public class Andar2 extends Piso{

    @Override
    public IEstado sobe() {
        return new Andar3();
    }

    @Override
    public IEstado desce() {
        return new Andar1();
    }
    @Override
    public IEstado erro() {
                return new Manutencao(this);
    }
}
package me_me_elevador.logica;

public class Andar3 extends Piso{

    @Override
    public IEstado sobe() {
        //fico no mesmo estado
       return this;
    }

    @Override
    public IEstado desce() {
        //altero de estado
        return new Andar2();
    }
    @Override
    public IEstado erro() {
                return new Manutencao(this);
    }
}

package me_me_elevador.logica;

public class Elevador {
    IEstado estado;

    public Elevador() {
        estado = new RC();
    }

    public IEstado getEstado() {
        return estado;
    }
    public void setEstado(IEstado estado) {
        this.estado = estado;
    }
    public void sobe() {
        setEstado(estado.sobe());
    }

    public void desce() {
        setEstado(estado.desce());
    }
    
    public void erro(){
        setEstado(estado.erro());
    }
    
    public void chaveManutencao(){
        setEstado(estado.chaveManutencao());
    }
}

package me_me_elevador.logica;

public interface IEstado {
    IEstado sobe();
    IEstado desce();
    
    IEstado erro();
    IEstado chaveManutencao();
}

package me_me_elevador.logica;

public class Manutencao extends Piso{
    //vai recordar qual era o estado antes
    private IEstado anterior;

    public Manutencao(IEstado ant) {
        this.anterior = ant;
    }
    
    @Override
    public IEstado chaveManutencao() {
      return anterior;
    }   
}
package me_me_elevador.logica;

public abstract class Piso implements IEstado {
    @Override
    public IEstado sobe() {
        return this;
    }

    @Override
    public IEstado desce() {
        return this;
    }

    @Override
    public IEstado chaveManutencao() {
        return this;
    }

    @Override
    public IEstado erro() {
        return this;
    }
}
package me_me_elevador.logica;

public class RC extends Piso {

    @Override
    public IEstado sobe() {
        //altero de estado
        return new Andar1();
    }

    @Override
    public IEstado desce() {
        //fico no mesmo estado
        return this;
    }

    @Override
    public IEstado erro() {
        return new Manutencao(this);
    }
}

package me_me_elevador.util;

import java.util.Scanner;

public class IUUtil {
    public static int getOpcao(String question, String... options) {
        System.out.println(question);
        char[] ops = new char[options.length];
        for (int i = 0; i < options.length; i++) {
            char o = '\0';
            int c = 0;
            while (c < options[i].length()) {
                o = Character.toUpperCase(options[i].charAt(c));
                for (int j = 0; j < i; j++) {
                    if (ops[j] == o) {
                        o = '\0';
                        break;
                    }
                }
                if (o != '\0') {
                    break;
                }
                c++;
            }
            if (c >= options[i].length()) {
                return -1;
            }
            ops[i] = o;
            if (i > 0) {
                System.out.print(", ");
            }
            if (c > 0) {
                System.out.print(options[i].substring(0, c));
            }
            System.out.print("(" + ops[i] + ")" + options[i].substring(c + 1));
        }
        System.out.println();
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("Option: ");
            char o = Character.toUpperCase(sc.nextLine().charAt(0));
            for (int i = 0; i < ops.length; i++) {
                if (o == ops[i]) {
                    return i;
                }
            }
        }
    }
}

O erro e a chaveEmergencia são acções, sendo que o evento que surge é a Manutenção.

Tags : , ,

Programação avançada – capitulo 8 – maquina de estados, exemplo1

Exemplo de uma maquina de estado, simples, usando as referências a um mecanismo de um elevador

Em que ficou:

Com:

package me_me_elevador;

import me_me_elevador.IU.Texto.UsaElevador;
import me_me_elevador.logica.Elevador;

public class Me_ME_elevador {
    public static void main(String[] args) {
        Elevador e = new Elevador();
        UsaElevador usaE = new UsaElevador(e); //interface com o utilizador
        usaE.corre();
    }
}
package me_me_elevador.IU.Texto;

import me_me_elevador.logica.*;
import me_me_elevador.util.IUUtil;

public class UsaElevador {

    //o interface com o utilizador, com o nosso elevador.
    private Elevador elevador;
    private boolean sair = false;

    public UsaElevador(Elevador elevador) {
        this.elevador = elevador;
    }

    public void corre() {
        while (!sair) {
            //saber em que estado estou
            IEstado estado = elevador.getEstado();
            if (estado instanceof RC) {
                iuRC();
            } else if (estado instanceof Andar1) {
                iuAndar1();
            } else if (estado instanceof Andar2) {
                iuAndar2();
            } else if (estado instanceof Andar3) {
                iuAndar3();
            }
        }
    }

    private void iuRC() {
        //opcoa 1
        //pedir ao utilizador: sair ou subir
        int opcao = IUUtil.getOpcao("Piso R/C", "Ir embora", "Subir");
        switch (opcao) {
            case 0:
                sair = true;
                break;
            case 1:
                //fazer evoluir a maquina de estados
                elevador.sobe();
                break;
        }
    }

    private void iuAndar1() {
        int opcao = IUUtil.getOpcao("Piso 1 andar","Descer", "Subir");
        switch (opcao) {
            case 0:
                //fazer evoluir a maquina de estados
                elevador.desce();
                break;
            case 1:
                //fazer evoluir a maquina de estados
                elevador.sobe();
                break;
        }
    }

    private void iuAndar2() {
        int opcao = IUUtil.getOpcao("Piso 2 andar","Descer", "Subir");
        switch (opcao) {
            case 0:
                //fazer evoluir a maquina de estados
                elevador.desce();
                break;
            case 1:
                //fazer evoluir a maquina de estados
                elevador.sobe();
                break;
        }
    }

    private void iuAndar3() {
        int opcao = IUUtil.getOpcao("Piso 3 andar","Descer");
        switch (opcao) {
            case 0:
                //fazer evoluir a maquina de estados
                elevador.desce();
                break;
        }
    }
}
package me_me_elevador.logica;

public class Andar1 extends Piso{

    @Override
    public IEstado sobe() {
        //altero de estado
        return new Andar2();
    }

    @Override
    public IEstado desce() {
        //altero de estado
        return new RC();
    }
}
package me_me_elevador.logica;

public class Andar2 extends Piso{

    @Override
    public IEstado sobe() {
        return new Andar3();
    }

    @Override
    public IEstado desce() {
        return new Andar1();
    }
}
package me_me_elevador.logica;

public class Andar3 extends Piso{

    @Override
    public IEstado sobe() {
        //fico no mesmo estado
       return this;
    }

    @Override
    public IEstado desce() {
        //altero de estado
        return new Andar2();
    }
}
package me_me_elevador.logica;

public class Elevador {
//é a maquina de estados, que faz alterar os estados
    IEstado estado;

    public Elevador() {
        //o estado incial//arranque é então:
        estado = new RC();
    }

    public IEstado getEstado() {
        return estado;
    }

    public void setEstado(IEstado estado) {
        this.estado = estado;
    }

    //podia ter outras informações, como o numero de pessoas
    //as luzes que estão ou não ligadas..
    //métodos que dão origem à mudança de estado
    public void sobe() {
        setEstado(estado.sobe()); //versão1
        //this.estado = estado.sobe();  //versão2
    }

    public void desce() {
        setEstado(estado.desce()); //versão1
        //this.estado = estado.desce();  //versão2
    }
}
package me_me_elevador.logica;

public interface IEstado {
    //acções comuns a cada uma dos estados:RC,andar1,andar2...
    //interface geral
    IEstado sobe();
    IEstado desce();
}
package me_me_elevador.logica;

public abstract class Piso implements IEstado {
    //classe para implementação default, sem alterar o estado
    //serve para simplifcar todas as outras
    @Override
    public IEstado sobe() {
        return this;
    }

    @Override
    public IEstado desce() {
        return this;
    }
}
package me_me_elevador.logica;

public class RC extends Piso{

    @Override
    public IEstado sobe() {
        //altero de estado
        return new Andar1();
    }

    @Override
    public IEstado desce() {
        //fico no mesmo estado
        return this; 
    }
}
package me_me_elevador.util;

import java.util.Scanner;

public class IUUtil {
    // v1
      public static int getOpcao(String question, String... options) {
        System.out.println(question);
        char[] ops = new char[options.length];
        for (int i = 0; i < options.length; i++) {
            char o = '\0';
            int c = 0;
            while (c < options[i].length()) {
                o = Character.toUpperCase(options[i].charAt(c));
                for (int j = 0; j < i; j++) {
                    if (ops[j] == o) {
                        o = '\0';
                        break;
                    }
                }
                if (o != '\0') {
                    break;
                }
                c++;
            }
            if (c >= options[i].length()) {
                return -1;
            }
            ops[i] = o;
            if (i > 0) {
                System.out.print(", ");
            }
            if (c > 0) {
                System.out.print(options[i].substring(0, c));
            }
            System.out.print("(" + ops[i] + ")" + options[i].substring(c + 1));
        }
        System.out.println();
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("Option: ");
            char o = Character.toUpperCase(sc.nextLine().charAt(0));
            for (int i = 0; i < ops.length; i++) {
                if (o == ops[i]) {
                    return i;
                }
            }
        }
    }
}

No interface IEstado, são colocados todas as ações dos estados, mesmo que não façam sentido em alguns dos estados.
A class Piso, faz a implementação default de alguns dos estados para que não tenhamos que estar a repetir código nos diferentes estados.

Tags : , ,

JavaFX, construir aplicações com GUI em JAVA

Para começar a trabalhar com o JavaFX é necessário instalar na máquina:
https://openjfx.io/

que muito provavelmente vai redireccionar para o link:
https://gluonhq.com/products/javafx/

o download deve ser de:
openjfx-14.0.1_windows-x64_bin-sdk.zip

(ou então de uma versão LTS)

Em Windows depois é necessário descompactar o ZIP e copiar a pasta para:
\Program Files\Java

Antes de passar ao passo seguinte, recomendo que se instale a versão mais recente do Java, por exemplo a jdk-14.0.1_windows-x64_bin.exe e que desinstale todas as outras versões!!

Agora falta configurar o netbeans, v13:
criar uma nova biblioteca, tools->libraries
botão: new library
atribuir um nome, por exemplo JavaFX14
clicar em Add/JAR/Folder
e adicionar (seleccionar) todos os ficheiros jar da pasta: javafx-sdk-14.0.1\lib
voltar à janela principal do netbeans, e ir às propriedades do projeto
nas propriedades do projeto, escolher  a opção libraries e clicar em classpath e libraries
escolher a opção JavaFX14
e no separador Run adicionar ao modulepath adicionar a biblioteca JavaFX14
ainda nas propriedades e do lado esquerdo, escolher Run, VM options:
–module-path /Program Files/Java/javafx-sdk-14.0.1/lib –add-modules javafx.controls

e done!

(ou seguir isto https://openjfx.io/openjfx-docs/#IDE-NetBeans)

 
package javafx01;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class JavaFx01 extends Application {
    public static void main(String[] args) {
        //passamos os argumentos para o subsistema gráfico os parametros
        launch(args); 
    }

    @Override
    //quando começa a aplicação
    public void start(Stage stage) throws Exception{
        BorderPane bp = new BorderPane();
        Label label1 = new Label("programação avançada");
        Label label2 = new Label("outra label");
        bp.setBottom(label1);
        bp.setTop(label2);
        //criar a cena
        Scene scene = new Scene(bp,400,400); //raiz da cena, e dimensões
        //adicionar a cena ao palco
        stage.setScene(scene);
        stage.show();
    }
}

Stage -> a janela da aplicação
Scene -> representa os elementos que vão estar presentes num stage
Node -> componentes específicos, são os nodes, que podemos ter dentro de cada cena (Canvas, ImageView, MediaView, Shape, Shape3D, SwingNode, EventTarget, Styleable: button, checkbox, combobox, label, listview, progressbar, radiobutton textfield)

Acerca dos objectos Pane existem estas opções:

Para projectos futuros:
em libraries->separador compile-> class path: e seleccionar a biblioteca JavaFX14
separador run -> modulepath: e seleccionar a biblioteca JavaFX14
e no run -> VM options -> –add-modules javafx.controls

Tags : , ,

ukie, you-key, (UK Interactive Entertainment)

Mais um grupo relacionado com a industria dos videojogos, com algumas referências para investigação feita.. um grupo organizado no UK.

+infos(oficial): https://ukie.org.uk/

Tags : , , ,

European Games Developer Federation Ekonomisk Förening (EGDF)

Mais um grupo relacionado com a industria dos videojogos, não têm referências para investigação feita.. mais parece um aglomerado de empresas para obter uma força positiva para esta industria!

+infos(oficial): http://www.egdf.eu/

Tags :

Uma lista colaborativa acerca de ferramentas open-source #opensource

Tiny/weirdo game engines:

Game engines that let you make a full game, but often within a particular style, ethos, framework, or with other limitations. Generally quick to pick up and work with, made for accessibility.

  • Flickgame – A truly tiny engine, link frames to other frames. Share online or export. 
  • GB Studio – A drag and drop gameboy interface that lets you do some scripting. Export to html or an actual gameboy rom! 
  • Bitsy – Bitsy is a simple sprite-based editor that lets you build rooms & worlds. Walk around and talk to people and be somewhere. Has a strong community.
  • Borksy / Bitsy HD / bitsy hacks / bitsy mixer template – Hacks that extend the functionality of bitsy in various ways.
  • Flicksy – A tool for drawing and assembling graphical hypertext games – lets you import drawings!
  • Mosi – Similar to bitsy, but with more color & sound support, and more advanced scripting options.
  • Bravitzlana – A tool for making small interactive scenes (3d! kind of) that you can share with people.
  • PuzzleScript – An open source, HTML5 puzzle-game oriented editor.
  • Dungeonscript – Puzzlescript, but first person
  • RPG Paper Maker – An extremely cute jrpg-focused engine that lets you place flat sprites in a 3d world. (free, but $70 if you sell your game commercially)
  • Flatgames / Flatpack – Flatgames are as much a concept as engine (2d, a raw combination of movement, art and sound), but Flatpack bundles those ideas up into a tool that you can import into Unity or run on Android.
  • Tiny Game Maker – For small, one screen games without programming.
  • Kooltool – An experimental game making tool which has Kidpix vibes.
  • The Unfolding Game Engine – One to watch (in early access right now). A “paint a world” engine that lets you make your world while playing it. 2.5d. 
  • Multiverse – Not out yet – a storytelling and game making tools/game.
  • Bitmelo – A game editor and engine for making small pixel art games. In early access, exports to HTML5. 
  • Sok Worlds ($3) – A truly wild game/tool where you make and explore 3d collage worlds (images from the pixabay API, but there are over a million to choose from.).
  • Sok Stories ($3) – A drawing-based way to make and share games.
  • Playscii – Also an ascii art and animation program, but the game mode documented here: http://vectorpoem.com/playscii/howto_game.html 
  • Wick Editor – Also an animation program- a friendly flash-like for multimedia projects that allows interaction and game-like play. 
  • Unicorn Console – A “a quick and dirty engine … with a resolution of 400×240 pixels.”
  • Vipercard – An open source reimagining of 1987’s Hypercard.
  • Twine – An open-source tool for telling interactive, nonlinear stories. Has a big community and multiple versions and themes.
  • Choicescript  – Like Twine, but for more traditional CYOA gamebooks-with-stats. Tight community on the forums.
  • Tiny Choice – The tiniest of twine-likes, written in the browser.
  • Inform – A design system for interactive fiction which has been around for a while!
  • Ink – A narrative engine designed to slot into a game engine.
  • StudyCrafter – A scratch-like platform where you can play, share, and create interactive projects, on the browser or offline, and collect data from players. 
  • Inklewriter – The “baby” Ink, runs in browser and great for CYOA, same UI as Sorcery!
  • The Adliberum Engine – (Free but on Steam, early access) Make text adventures, muds and text-powered roleplaying games.
  • Yarn Spinner – The friendly tool for writing game dialogue. 
  • Cheap Bots Done Quick – A site where you can make a twitterbot today.

Indie/open source bigger game engines: 

Indie and open source game engines with more flexibility and power than the little engines above, and generally also another layer of complication. (Perhaps most useful for if you are concerned about free but closed-source engines like Unity.)

  • Superpowers – HTML5 2d + 3d engine, coding in Typescript.
  • HaxeFlixel – Cross-platform development, open source
  • Godot – Perhaps the most complete and well documented open source engine, for 2d and 3d.
  • Heaps – A free and open source cross platform graphics engine written in Haxe.
  • DOME – A framework for making 2D games using the Wren programming language.
  • luxe – In development: a 2d-focused engine, code in Wren.
  • LITIENGINE – A free and open source Java 2D Game Engine.
  • Starling – A Cross Platform Game Engine.
  • LÖVR – An open source framework for rapidly building VR experiences in Lua.
  • Ren’Py – Free, open source cross-platform Visual Novel development engine.
  • Adventure Game Studio – Free engine for making point & click adventure games.
  • Phaser.io – Desktop and mobile HTML5 game framework, using javascript.
  • Pixelbox.js – Combination editor and a simple JavaScript library.
  • Amulet – A free Lua-based audio/visual toolkit suitable for small games and experimentation, with online editor.

Fantasy consoles: 

A fantasy console is like a regular console (machine specs, dev tools, community), but without hardware. It is like an emulator for a machine that never existed. Generally, these are in the form of an application.

  • Pico8 ($15) – probably the most popular fantasy console for game dev, pico8 has harsh limitations but that are intentionally chosen. code is written in lua. export to standalone HTML+JS or PNG (fantasy cartridge, need pico8 to play)
  • Tic-80 – coding in lua and moonscript. export to html or .tic. 
  • Pixel Vision 8 (free, pro version is $30) – a no-console fantasy computer, navigate like the familiar icon-based desktops you know. 
  • LIKO-12 – Entirely open-source and free, written in Lua. The dev says: “Why did I develop this? Because I wanted to buy PICO-8 but that’s not possible without credit cards (no internet shopping in Syria)”
  • Pix64 – an extremely tiny fantasy console (64×64 px).
  • Homegirl Pro – A fantasy console with a very different vibe! This one dispenses of pixel nostalgia and is modeled after a Commodore Amiga. Coding in Lua. 
  • VectorBoy – Again, a fantasy console that breaks the mold – VectorBoy uses straight lines to emulate vector graphics.
  • VVpet – A fantasy console for LCD virtual pet games
  • Voxatron – ($20, come with Pico-8) Fantasy Console for voxel (3d pixel, kind of?) games. 
  • LowRes NX – BASIC programming on iOS, as well as desktops.
  • Pyxel – A retro game engine for Python
  • Zany80 – A fantasy console designed around the Z80 processor.
  • Riko4 – A Fantasy Console intended as a tool for pixel art game development.
  • ECoS – A modern fantasy console with Entity-Component-System modular architecture.
  • Nano JAMMER – A truly tiny console that runs in the browser and integrates into Google Drive. Code in the nano programming language. 
  • Rewtro – A wild little console that runs games encoded in a very small amount of data, meaning they can be printed as qr-codes.
  • Script-8 – A fantasy computer for making, sharing, and playing tiny retro-looking games. Free, browser-based, and open-source. Code in Javascript

Didn’t find what you were looking for? Just into fantasy consoles? More of them detailed here: https://github.com/paladin-t/fantasy 

Neat Unity extenders:

Tools or plug-ins that change the experience of using Unity.

  • Oikospiel-tools – Trigger-based tools for Unity for crafting games without programming.
  • Unity Bitmap Drawing – A library for adding real-time drawing to your Unity project.
  • Clayxels – Adds tiny voxels that seamlessly blend together like clay.
  • unity-wave-function-collapse – Bitmap & tilemap generation from a single example.
  • Tessera (5$)- 2d / 3d wave function collapse generation.
  • Borderless Unity Windows – A tiny tool for making Unity frameless apps from script.
  • First Person Drifter – The original drifting game controller. Download an updated package for Unity 2019 from me here
  • Unity Recorder – An editor-only tool that captures video and animation data during gameplay.
  • Bobbin – A small Unity editor tool that can automatically download and import anything with a URL into the project.
  • Meshedit ($35) – A Unity extension that lets you create and texture models from scratch.
  • Doodlestudio 95 ($45) – A FUN drawing and animation tool for Unity.
  • Raymarching Toolkit ($75) – A Unity add-on for editing raymarched scenes live.
  • Path CreatorPath creation asset for Unity game development
  • NaughtyAttributes – Create powerful inspectors without the need for custom editors.
  • UCLA Game Lab Mesh Creator – Extrudes 2D drawings into 3D objects.
  • Rhythm beat mapper ($45) – Synchronizes gameplay to music in your video game.
  • JPEG-MP4-Compression – recreates the effect of JPEG/MP4 compression as a PostProcessing Effect.

Godot Extenders:

Tools or plug-ins that change the experience of using Godot.

  • WAT – An automated testing framework for Godot built entirely within Godot itself

Maps, place, & levels:

Tools for making maps & levels – some of them simply visual, others generate data.

  • Tiled – The standard free, easy to use and flexible level editor.
  • OGMO – A free, open source, project oriented level editor. 
  • Tilesetter – Tileset designing made easy.
  • SPARTAN Procedural Tile Generator – A pixel art drawing and animation tool specialised in creating tiles and animated sprites for games.
  • Medieval Fantasy Town Generator – Generates a random medieval city layout of a requested size & can export to JSON.
  • City Viewer – The city viewer to see the above/other maps in 3d!
  • One Page Dungeon – A very simple one page dungeon generator.
  • Map Generator – A cellular automata tool for building custom maps, envisioned for use in tabletop roleplaying games.
  • Paper Dungeon Maker – A very little map generator with optional csv output.
  • RPG Map II – A tabletop oriented map editor with clean and simple design.
  • Snazzy Maps – Nice map stylings for Google Maps.
  • Google Earth Studio – Automate flying about the world.
  • Topo Topo – Get a little square of 3d elevation data, anywhere.
  • Nototo – Build and visualize all your notes, as a map
  • terrain.party – nice interface for getting terrain heightmaps anywhere in the world

+infos(fonte): http://everest-pipkin.com/teaching/tools.html

Tags : , ,