0
\$\begingroup\$

I have written a version of Castle that so far plays through the terminal. I have recently refactored my code (which can be found here) to use the MVC design pattern. After I have added a GUI I will add networking capabilities. The game at the moment should be thought of as a local single (human) player game against AI's.

The game controller holds the gameState, gameView and then the business logic to run the game. The business logic is essentially just a loop that loops over the players in turn telling each to pick a move, applying the move to the state and then telling the view to print the state (to the terminal atm).

The game is made up of the 3 types of players:

  • An Information Set MCTS AI player
  • AI player that just plays a basic strategy of playing the lowest possible card(s)
  • Human player

The AI players have a controller that holds the player model and business logic and the player view (but only for human players). The player model is a generic POJO containing hand, castle cards etc. The logic is different from player to player but is all held in the controller. The game controller calls playerController.getMove(), the player then chooses their move depending on their strategy/implementation and returns it to the gameController.

The 'Move' objects are created by the playerController and passed back to the gameController and hold information like the card played or if they can't play then its a PickUp move etc. The move objects hold the choices made by the player and the business logic of how they change the game state.

  1. GameController calls player.getMove(gameState)
  2. PlayerController selects a move and returns it
  3. Game controller calls move.doMove(gameState)
  4. Next player until someone wins

My first question is, is it ok that the 'Moves' store logic or is there a better way? They are the only components that don't strictly follow MVC.

Secondly and more importantly, I can't seem to get my head around how the game runs using JavaFX as a GUI. I haven't got any experience with JavaFX until now and little UI experience in general but I'm trying. I've read that the Controller/View can become a bit convoluted. I've tried to find videos and resources to help give me some understanding but none seem to be in quite the right context - turn based, animationless (for now), card game. In my head I imagine the GUI displaying the players cards face up and the opponent's cards hidden but displayed with their backs up. The game waits for the human to select the card(s) to play and validates them. Then it updates to show the move completed. On AI opponent turns it waits until a move has been chosen and then updates on screen. But where does this go and how does it interact? I think the GameController/View should display the game state for the player to see and interact with? Is the playerView unnecessary then?

I think I'm struggling to get my head around how the GUI is running all the time for the player and doesn't care about their input until its their turn and how this fits in with the game loop I have created so far.

EDIT:

I have managed to create a scene with ListView's to display the players' cards that have been dealt to them. However, I cannot figure out how my game loop is added to the code.

    boolean print = true;
    boolean gameOver = false;

    do {
        for (PlayerController player : players) {
            if (gameOver) {
                break;
            }
            boolean playersTurn = true;
            while (playersTurn) {

                CastleMove move = player.getMove(gameState);
                doMove(move);

                // If the player does not burn the pile then it is the next players turn
                if (!move.burnsPile()) {
                    playersTurn = false;
                }
                if (gameState.isGameOver()) {
                    gameOver = true;
                    break;
                }
            }
        }
    } while (!gameOver);

If I add the code to the start() method then the window displaying the cards never shows because it hasn't been executed yet. Where does it go?

\$\endgroup\$
3
  • \$\begingroup\$ ...uhm... it will be hard to answer all that in one question :-& \$\endgroup\$ Feb 13, 2020 at 14:27
  • \$\begingroup\$ @MartinFrank Maybe it is easier to answer with my newest edit. \$\endgroup\$
    – theJavaGuy
    Feb 13, 2020 at 15:13
  • \$\begingroup\$ see also gamedev.stackexchange.com/questions/179224/… - similar case \$\endgroup\$ Feb 21, 2020 at 8:52

1 Answer 1

0
\$\begingroup\$

if you want to implement a minimal M-VC you have to structure your application kind a like this:

example Diagramm

(This is only an example Diagramm)

Model

The Game is your Model. It stores information about who's turn is it now or how many cards are on this stack (or on that stack). It also provides minimal methods to change these values, e.g. endTurn() turnes the next player into the current player or drawCard() takes a crad from the open stack and adds it into the players hand. It also provides Methods to get the Status of the Model, maybe a method getActivePlayer() will return the current Player, or maybe a method getOpenStack will give you a List of all the Cards form the open Stack of your Game.

IMPORTANT if you create a Model there is NO REFERENCE to the Controller allowed - believe me, this will help you in any further activities on your work. The Model should be a standalone!

Controller (With View)

The Controller is responsible for Drawing the Game and Handle Input - as you mentioned in your question, they have often similar responsibilities and resources.

The Controller in your case should be responsible for creating a GUI with Canvas and Buttons. Maybe it has also an animationThread that frequently updates the screen (for animations and other fancy stuff).

The second responsibility of the Controller would be to handle User Input. The Controller provides for example a button named @FXML Button endTurn;. Here is the controller responsible to grab the input from the User (mostly by attaching a Listener on that Button) and execute the method endTurn on the model side. The controller would also be responsible to execute an Update on the GUI after the button has been pressed, so that the current Player name would be displayed properly. Again, the value of the current Player would com from the model and the Controller would ask the model for the value (by calling the method getCurrentPlayer()on the model.

JavaFx Implemetation Hints:

public class App extends Application {

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

    private Pane root;

    @Override
    public void init() {
        Game game = new Game();
        ControllerFactory controllerFactory = new ControllerFactory(game);
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(getFxml());
            fxmlLoader.setControllerFactory(controllerFactory);
            root = fxmlLoader.load();
        } catch (IOException e) {
            //Log output please (I use a Logger for that)
        }
        RootController rootController = controllerFactory.getRootController();
        game.init();
        rootController.init();
    }

    @Override
    public void start(Stage stage) {
        if (root != null) {
            stage.setScene(new Scene(root));
            stage.setTitle("title");
            stage.show();
        } else {
            //Log output please (I use a Logger for that)
            Platform.exit();
            System.exit(0);
        }
    }

}
public class ControllerFactory implements Callback<Class<?>, Object> {

    private final Game game;

    private RootController rootController;

    public ControllerFactory(Game game) {
        this.game = game;
    }

    @Override
    public Object call(Class<?> type) {
        if (type == RootController.class) {
            //inject the Model in the controller
            rootController = new RootController(game); 
            return rootController;
        } else {
            // default behavior for controllerFactory:
            try {
                return type.getDeclaredConstructor().newInstance();
            } catch (Exception exc) {
                exc.printStackTrace();
                throw new RuntimeException(exc); // fatal, just bail...
            }
        }
    }

    public RootController getRootController() {
        return rootController;
    }


}

Conclusion What this points out is that the controller is depending on the Game, but the Game is independent from the Controller.

Think of it: you can create new Controllers for any GUI: Android, Console, JavaFX, Swing, TCP/IP, whatever ^_^ - all without ever changing the Game-class!

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .