# UI, Controller and Gameplay This page describes the inteded development cycle of a [complex interactive program](./the_inversion_of_control_problem.html#complex-interactive-subsystems). It does so by showing where Rulebook code would fit into into a [model view controller scheme](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) (MVC). Notice: Rulebook is not specific to a MVC scheme. We just use MVC because that is what many people are familliar with and we cannot describe how Rulebook would fit in every software architecture in existance. ## MVC ```{tikz} :libs: arrows.meta,calc,positioning \begin{tikzpicture}[ every node/.style = {font=\sffamily\small, rounded corners, draw, thick, minimum width=2.9cm, minimum height=1.3cm}, node distance = 50mm, >=Stealth, very thick, % arrow styles ltr/.style = {->, below, font=\scriptsize}, % left-to-right (below nodes) rtl/.style = {->, above, font=\scriptsize} % right-to-left (above nodes) ] \def\vsep{12mm} % vertical separation for the curved paths % --- nodes -------------------------------------------------------------- \node[fill=blue!15] (model) {Model}; \node[fill=green!20, right=of model] (controller) {Controller}; \node[fill=orange!20, right=of controller] (view) {View}; % --- data-flow arrows --------------------------------------------------- % left-to-right • below nodes • attach to east/west corners \draw[ltr] (model.south east) .. controls +(0,-\vsep) and +(0,-\vsep) .. node[pos=.5] {Emits events} (controller.south west); \draw[ltr] (controller.south east) .. controls +(0,-\vsep) and +(0,-\vsep) .. node[pos=.5] {Update view} (view.south west); % right-to-left • above nodes • attach to west/east corners \draw[rtl] (controller.north west) .. controls +(0,\vsep) and +(0,\vsep) .. node[pos=.5] {Changes data} (model.north east); \draw[rtl] (view.north west) .. controls +(0,\vsep) and +(0,\vsep) .. node[pos=.5] {Tries to trigger user actions} (controller.north east); \end{tikzpicture} ``` Different people assign to MVC components different meanings. We are going to provide a definition for them that we will use for the rest of this document. ### Model The model is the collection of data required to perform the business objective of the software. The model can be internal to the process or can be remote. It can be persistent or bound to the lifetime of the program only. It can be serializable or not. Examples of models are: * the content of a word document in microsoft word. * the in memory state of a video game, including the part that gets stored in a safe file, and the parts that are short lived but important for the rules of the game. ### View The view is the component that knows how to display to the user (or to another program), the relevant parts of the model so that the user can execute decisions. The view responds to user actions (such as button presses) by issuing commands to the controller. The view get updated by the controller whenever the controller deems relevant to do so. This could be every frame like in videogames, or after every modification of the underlying model, such as in CRUD applications. Views are usually short lived and not stored on disk. Views can be local or remote. Examples of views are: * the entire graphical stack in a video game, which at each frame takes part or all of the state of the game (the model) and spits out a image rappresenting it. * a web-ui for some checkout procedure in a web shop, where the user provides data, click ok, and then is brought to a new screen until the purchage is compleated. ### Controller The controller is the part of the program that knows the business logic of the program. It knows how to read at modify the model to carry out the program objective. Controllers are usually short lived and not stored on disk, except for some user configuration, for example the language of the user, or the dark/light theme preference of the user. The controller can observe the model data to get notifications about when some of that data changes, so that additional logic can be triggered. The controller knows how to update the view in response to changes in the model, and the controller knows how to reject invalid actions issues by the view. Examples of controllers are: * the section of the program in a meteo related tool that forecasts the weather, and when it is done doing so saves the prediction in the model, and shows it on screen. * the logic that drives the rules of chess indipendantly from the content of the screen. ## VIEW-Rulebook pattern The simplest, but powerfull way of combining Rulebook with MVC is to subsume the Model and the Controller in RLC. Variables inside Rulebook action functions become the model, the action function itself becomes the controller. The view sends actions to the action function, and since the action function knows all valid actions, it rejects those that are not. After each action, or series of batched actions, the view updates itself from scratch. ```{tikz} :libs: arrows.meta,calc,positioning \begin{tikzpicture}[ every node/.style = {font=\sffamily\small, rounded corners, draw, thick, minimum width=3.4cm, minimum height=1.6cm}, node distance = 55mm, >=Stealth, very thick, % arrow styles ltr/.style = {->, below, font=\scriptsize}, % left-to-right (below nodes) rtl/.style = {->, above, font=\scriptsize} % right-to-left (above nodes) ] \def\vsep{14mm} % vertical separation for the curved paths % ------------------------------------------------------------------------ % NODES % ------------------------------------------------------------------------ \node[fill=cyan!15] (rlc) {Rulebook}; \node[fill=orange!20, right=of rlc] (view) {View}; % ------------------------------------------------------------------------ % FLOWS % ------------------------------------------------------------------------ % right-to-left • above nodes • view → RLC (send actions) \draw[rtl] (view.north west) .. controls +(0,\vsep) and +(0,\vsep) .. node[pos=.5] {actions (validated / rejected)} % label (rlc.north east); % left-to-right • below nodes • RLC → view (full refresh) \draw[ltr] (rlc.south east) .. controls +(0,-\vsep) and +(0,-\vsep) .. node[pos=.5] {refresh view from scratch} % label (view.south west); \end{tikzpicture} ``` Altough slow since the view is recalculated entirelly, this pattern provides off-the-shelf. * automatic textual and binary serialization of the model. * automatic tracing, and textual and binary serialization for the tracing o f the controller. * automatic testing with fuzzers, machine learning and proofs. * automatic bindings to execute the view indipendently from rulebook. * automatic bindings to write the view in multiple languages. ## Efficient VIEW-Rulebook pattern This is the pattern implemented by our example [4Hammer](./4hammer.md#software-architecture). As we said the VIEW-Rulebook pattern suffers because it updates the view at every user input, leading to bad performances in complex views. We offer two ways of mending the problem. The first solution retains the entirety of the benefits of the VIEW-Rulebook pattern, and consists in emitting events to which the view can subscribe too and process at their own will. For example, if you need a more performant implementation of chess for some reason, the Rulebook components can mantain along side the game a queue of events, such as "piece moved", "piece removed". After every action the view consumes these events and updates itself. So a graphical element showcasing a rook can simply subscribe to the "piece moved" event and update itself it the piece that was moved was indeed its own. ```{tikz} :libs: arrows.meta,calc,positioning \begin{tikzpicture}[ every node/.style = {font=\sffamily\small, rounded corners, draw, thick, minimum width=3.4cm, minimum height=1.6cm}, node distance = 55mm, >=Stealth, very thick, % arrow styles ltr/.style = {->, below, font=\scriptsize}, % left-to-right (below nodes) rtl/.style = {->, above, font=\scriptsize} % right-to-left (above nodes) ] \def\vsep{14mm} % vertical separation for the curved paths % ------------------------------------------------------------------------ % NODES % ------------------------------------------------------------------------ \node[fill=cyan!15] (rlc) {Rulebook}; \node[fill=orange!20, right=of rlc] (view) {View}; % ------------------------------------------------------------------------ % FLOWS % ------------------------------------------------------------------------ % right-to-left • above nodes • view → RLC (send actions) \draw[rtl] (view.north west) .. controls +(0,\vsep) and +(0,\vsep) .. node[pos=.5] {actions (validated / rejected)} % label (rlc.north east); % left-to-right • below nodes • RLC → view (full refresh) \draw[ltr] (rlc.south east) .. controls +(0,-\vsep) and +(0,-\vsep) .. node[pos=.5] {Events} % label (view.south west); \end{tikzpicture} ``` The second more performant implementation allows the Rulebook code to direclty call the code that updates the view, without any kind of indirection. That pattern breaks many of automatic tools offered by rulebook, but they can mostly be mended. * automatic tracing, and textual and binary serialization for the tracing of the controller. **unchanged** * automatic bindings to write the view in multiple languages. * automatic bindings to execute the view indipendently from rulebook. **meaningless**, the code was designed to have no real separation between view and controller. If the tool requires multiple views, for example a web view and a integrated view, then the same techinques described for machine learning can be used. * automatic testing with fuzzers, machine learning and proofs. **preserved with conditional compilation**: The compiler can be configured to not emit the code specific to the view when building the Rulebook components for fuzzers, machine learning and proofs. If the machine learning tools are to be used on a released build with the engine enabled, it is possible to specify that view related memory objects, such as pointers to the view components to update after a model change, are to be ignored by the machine learning. * automatic textual and binary serialization of the model. **requires to specify which are view datastructures**: eventual view related datastructure inside the model can be marked as to be ignored during serialization and deserialization. After they are loaded from disk, the view must somehow repopulate them correctly. ## View-Rulebook-Model Pattern All discussion about the view expressed in the previous sections are still valid for this section, which instead focuses on the indipendent problem of mixing Rulebook with custom models. The requirement for a custom model usually happen when you want partially or totally: * reuse previously existing code to implement the model. * move the model into another process, for example because it is actually held in a remote database. * write the model in some other language because of performance or some other requirement (CUDA, SQL...). In this situations you can treat problem of connecting Rulebook to a external language, as explained [here][ToDo]. ```{tikz} :libs: arrows.meta,calc,positioning \begin{tikzpicture}[ every node/.style = {font=\sffamily\small, rounded corners, draw, thick, minimum width=2.9cm, minimum height=1.3cm}, node distance = 50mm, >=Stealth, very thick, % arrow styles ltr/.style = {->, below, font=\scriptsize}, % left-to-right (below nodes) rtl/.style = {->, above, font=\scriptsize} % right-to-left (above nodes) ] \def\vsep{12mm} % vertical separation for the curved paths % --- nodes -------------------------------------------------------------- \node[fill=blue!15] (model) {Model}; \node[fill=green!20, right=of model] (controller) {Rulebook}; \node[fill=orange!20, right=of controller] (view) {View}; % --- data-flow arrows --------------------------------------------------- % left-to-right • below nodes • attach to east/west corners \draw[ltr] (model.south east) .. controls +(0,-\vsep) and +(0,-\vsep) .. node[pos=.5] {Emits events} (controller.south west); \draw[ltr] (controller.south east) .. controls +(0,-\vsep) and +(0,-\vsep) .. node[pos=.5] {Update view} (view.south west); % right-to-left • above nodes • attach to west/east corners \draw[rtl] (controller.north west) .. controls +(0,\vsep) and +(0,\vsep) .. node[pos=.5] {Changes data} (model.north east); \draw[rtl] (view.north west) .. controls +(0,\vsep) and +(0,\vsep) .. node[pos=.5] {Tries to trigger user actions} (controller.north east); \end{tikzpicture} ``` Just like when violating the separation of view and controller, writing parts of the model outside of rulebook makes it hard to use off-the-shelf rulebook tools. ### automatic textual and binary serialization of the model. Relies on having implemented the method to print, parse, binary print and binary parse the model. If you can provide those in the original language in every class that you use in Rulebook from another language, then you will get this functionality to work. ### automatic tracing, and textual and binary serialization for the tracing o f the controller. As long as you don't use classes defined in some other language within action statments arguments, this will work. If you do use them there, then the same consideration for the previous point apply. ### automatic testing with fuzzers, machine learning and proofs. Fuzzers work as the previous point. On top of that: * Model checking proofs require that model classes written in another are copiable. * Machine learning requires that your model classes written in another language implement the serialization to tensor traits. ### automatic bindings to execute the view indipendently from rulebook. Unafected. ### automatic bindings to write the view in multiple languages. Unafected. ## View-Multi Rulebook Controller pattern While some programs may make sense to think in terms of a single gigantic interactive sequence from start to finish, this is usually not the case for most programs. Interactive programs usually have many small interactive sequences that the user can perform. For example, a typical videogame may have a interactive sequence describing in which order powerups can be unlocked by a player, and another interactive sequence is the way a non player character behaves in respose to the player interacting with that character. You could stick them all into a single enormous interactive sequence that allows to perform any action from any of al sub interactive sequences at the same time, or, instead you just consider them different controllers of different parts of the model. In this situation, all considerations previously mentioned in this article still apply. The only difference is that many Rulebook controllers are placed inside a "controller aggregator" written in some other language (or even rulebook itself). ```{tikz} :libs: arrows.meta,calc,positioning,fit,backgrounds \begin{tikzpicture}[ every node/.style = {font=\sffamily\small, rounded corners, draw, thick, minimum width=3.0cm, minimum height=1.35cm}, node distance = 55mm, >=Stealth, very thick, bi/.style = {<->} % bidirectional edge ] % ---------------------------------------------------------------- % RULEBOOK CONTROLLERS (stacked) --------------------------------- % ---------------------------------------------------------------- \node[fill=green!20] (rb1) {Player Behaviour \#1}; \node[fill=green!20, below=12mm of rb1] (rb2) {Player Story Progression \#2}; \node[fill=green!20, below=12mm of rb2] (rb3) {World Controller \#3}; % ---------------------------------------------------------------- % CONTROLLER-AGGREGATOR (surrounds the Rulebooks) ---------------- % ---------------------------------------------------------------- \begin{scope}[on background layer] \node[draw, rounded corners, very thick, fill=violet!12, inner sep=10mm, fit=(rb1)(rb2)(rb3), label={[font=\sffamily\small\bfseries]above:Controller aggregator}] (aggbox) {}; \end{scope} % ---------------------------------------------------------------- % MODELS (one per Rulebook) -------------------------------------- % ---------------------------------------------------------------- \node[fill=blue!15, left=55mm of rb1] (m1) {Model \#1}; \node[fill=blue!15, left=55mm of rb2] (m2) {Model \#2}; \node[fill=blue!15, left=55mm of rb3] (m3) {Model \#3}; % ---------------------------------------------------------------- % VIEW ------------------------------------------------------------ % ---------------------------------------------------------------- \node[fill=orange!20, right=55mm of aggbox] (view) {View}; % ---------------------------------------------------------------- % BIDIRECTIONAL EDGES -------------------------------------------- % ---------------------------------------------------------------- % Each Model ↔ its Rulebook \foreach \m/\r in {m1/rb1, m2/rb2, m3/rb3}{ \draw[bi] (\m.east) -- (\r.west); } % Aggregator ↔ View \draw[bi] (aggbox.east) -- (view.west); \end{tikzpicture} ``` The shown controller describes a interactive game where one rulebook controller controls how the player can act in the world, one controller handles a complex non linear story progression, and a indipendant world controller handles non player characters. Of course the astute reader will notice that this design describe a poor game where the player story is totally disconnected from the world controller, and this does not seem realistic. That brings us to the last MVC style design ## Multilayer Rulebook Controllers. Sometimes you have multiple interactive sequences that insist on the same model, and/or some those sequences may be allowed to create/cancel/modify another interactive sequence. For example, think of a video game where there is a interactive sequence which is the story, and another interactive sequence for each non player character on screen that handles the behaviour of that character. Say that when the user triggers a particular story event, all non player characters are to be destroyed, and the state of their interactive sequence, discarded. You can achieve this by organizing your interactive sequences hierarchically. The following image displays a setup where there exists a single model (implemented in rulebook or not), shared between all controllers. The non player characters controller is the least important one, that drives the behaviour of characters when the player is near. Then there is the story controller which is allowed create, modify and destroy enemies, and therefore has access to the non player characters controller. Finally there is the player controller that handles the user inputs and depending on the player action can trigger a new event of the story. ```{tikz} :libs: arrows.meta,calc,positioning,fit,backgrounds \begin{tikzpicture}[ every node/.style = {font=\sffamily\small, rounded corners, draw, thick, minimum width=3.2cm, minimum height=1.3cm}, node distance = 12mm, bi/.style = {<->, very thick}, % bidirectional edge down/.style = {->, very thick} % downward one-way edge ] % ---------------------------------------------------------------- % MODEL (shared) -------------------------------------------------- % ---------------------------------------------------------------- \node[fill=blue!15] (model) {Model}; % ---------------------------------------------------------------- % THREE RULEBOOK CONTROLLERS (stacked) ---------------------------- % ---------------------------------------------------------------- \node[fill=green!20, right=40mm of model] (pc) {Player controller}; \node[fill=green!20, below=8mm of pc] (sc) {Story controller}; \node[fill=green!20, below=8mm of sc] (npc) {NPC controller}; % ---------------------------------------------------------------- % CONTROLLER-AGGREGATOR (encloses the controllers) ---------------- % ---------------------------------------------------------------- \begin{scope}[on background layer] \node[draw, rounded corners, very thick, fill=violet!12, inner sep=10mm, fit=(pc)(sc)(npc), label={[font=\sffamily\small\bfseries]above:Controller aggregator}] (agg) {}; \end{scope} % ---------------------------------------------------------------- % VIEW (UI) ------------------------------------------------------- % ---------------------------------------------------------------- \node[fill=orange!20, right=40mm of agg] (view) {View}; % ---------------------------------------------------------------- % EDGES ----------------------------------------------------------- % ---------------------------------------------------------------- % Shared Model ↔ each controller \foreach \ctrl in {pc,sc,npc}{ \draw[bi] (model.east) -- (\ctrl.west); } % Hierarchical links: Player → Story → NPC (one-way) \draw[down] (pc.south) -- (sc.north); \draw[down] (sc.south) -- (npc.north); % View ↔ Player controller \draw[bi] (view.west) -- (pc.east); \end{tikzpicture} ``` While this pattern is more complex to write, it allows to create interactive sequences where sequences are allowed to modify each other, and it retains all the capabilities described in the [View-Rulebook pattern](#view-rulebook-pattern). As well as all considerations about dividing the controller from the model described earlier.