Initial commit of Mesos Java project

This commit is contained in:
2026-04-13 09:46:34 +02:00
commit f9d40590f8
76 changed files with 6785 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
target/
.idea/
*.iml
logs/

316
mvnw vendored Normal file
View File

@@ -0,0 +1,316 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`\\unset -f command; \\command -v java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

188
mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,188 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

112
pom.xml Normal file
View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Mesos2</artifactId>
<version>1.0-SNAPSHOT</version>
<name>MesosLL07</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.12.1</junit.version>
<javafx.version>21.0.6</javafx.version>
<log4j.version>2.23.1</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>11.2.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- JavaFX -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
</dependency>
<!-- Apache PDFBox per leggere il PDF -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.29</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version> <!-- Controlla se ci sono versioni più recenti, ma questa è ottima e sicura -->
</dependency>
<!-- Log4j 2 Core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>25</source>
<target>25</target>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<executions>
<execution>
<!-- Default configuration for running with: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<mainClass>org.example.mesosll07/org.example.mesosll07.HelloApplication</mainClass>
<launcher>app</launcher>
<jlinkZipName>app</jlinkZipName>
<jlinkImageName>app</jlinkImageName>
<noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<noHeaderFiles>true</noHeaderFiles>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,22 @@
package Server.Automaton;
public class ActionResult {
private final boolean success;
private final String errorMessage; // null se success
private ActionResult(boolean success, String errorMessage) {
this.success = success;
this.errorMessage = errorMessage;
}
public static ActionResult ok() {
return new ActionResult(true, null);
}
public static ActionResult failure(String message) {
return new ActionResult(false, message);
}
public boolean isSuccess() { return success; }
public String getErrorMessage(){ return errorMessage; }
}

View File

@@ -0,0 +1,53 @@
package Server.Automaton;
public class Automaton {
private GameState state = GameState.SETUP;
public GameState getState(){
return state;
}
private Boolean canEvolve(int val){
int currOrd = state.ordinal();
int setupOrd = GameState.SETUP.ordinal();
if (currOrd == setupOrd && val > 5) {
System.out.println("Invalid number of player");
return false;
}
return true;
}
public Boolean evolve(int i) {
if (!canEvolve(i))
return false;
int currOrd = state.ordinal();
int lastOrd = GameState.GAME_OVER.ordinal();
if (currOrd < lastOrd) {
state = state.next(i);
return true;
}
return false;
}
public Boolean evolveTo(GameState toState, int i){
if (!canEvolve(i))
return false;
int toOrd = toState.ordinal();
int currOrd = state.ordinal();
if (toOrd>currOrd) {
state = toState;
return true;
}
return false;
}
}

View File

@@ -0,0 +1,789 @@
package Server.Automaton;
import Server.*;
import Server.Cards.*;
import Server.Utils.LoadingCardsException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Central game controller for MESOS.
*
* Owns the full game lifecycle:
*
* SETUP → newGame()
* TOTEM_PLACEMENT → placeTotem() — one call per player, in TurnTile order
* ACTION_RESOLUTION → resolveCardAction() — one call per pending action
* EXTRA_DRAW → extraDrawAction() / skipExtraDraw() [only if a player owns the building]
* EVENT_RESOLUTION → handled automatically at end of round
* GAME_OVER → endGame()
*
* All public methods that advance state return a boolean:
* true = action accepted, state may have changed
* false = action rejected, state unchanged (caller should log / notify client)
*
* NOTE: GameState must have EXTRA_DRAW added to the enum for this class to compile:
* UNKNOWN, SETUP, TOTEM_PLACEMENT, ACTION_RESOLUTION, EXTRA_DRAW, EVENT_RESOLUTION, GAME_OVER
*
* NOTE: Tribe.endPoints() already computes end-game building bonuses internally via
* buildingAbilitiesEndPoints(). Do NOT also call BuildingManager.endgameForSix() etc.
* in endGame() — that would count those bonuses twice.
*/
public class Game {
private static final Logger logger = LogManager.getLogger(Game.class);
// -------------------------------------------------------------------------
// Constants
// -------------------------------------------------------------------------
private static final int TOTAL_ROUNDS = 10;
// -------------------------------------------------------------------------
// Core state
// -------------------------------------------------------------------------
private final List<Player> players;
private GameBoard gameBoard;
private int round;
private GameState state;
// -------------------------------------------------------------------------
// Phase tracking
// -------------------------------------------------------------------------
/** How many players have placed their totem this round. */
private int totemPlacedCount;
/** Index of the offering tile (central row) currently being resolved (left to right). */
private int currentOfferingTileIndex;
/** Actions the current player still has to resolve. */
private List<Symbol> pendingActions;
/**
* Queue of players who own the EXTRA_DRAW building and still need to
* make their extra-draw decision this round. Ordered by turn order.
*/
//TODO to check it should be a single player
private final List<Player> extraDrawQueue;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
public Game(List<Player> players) {
this.players = new ArrayList<>(players);
this.round = 0;
this.state = GameState.SETUP;
this.pendingActions = new ArrayList<>();
this.extraDrawQueue = new ArrayList<>();
}
// =========================================================================
// SETUP
// =========================================================================
/**
* Initialises a new game: loads the deck, builds the board, deals initial
* food, and randomises the first turn order.
*
* Initial food (rulebook setup step 10):
* Slot 1 -> 2 food, slots 2-3 -> 3 food, slots 4-5 -> 4 food.
*/
public void newGame(String cardsFilePath) {
try {
logger.info("STARTING NEW GAME : ", cardsFilePath);
CardDeck deck = new CardDeck();
deck.setForNPlayer(cardsFilePath, players.size());
gameBoard = new GameBoard(Era.I, deck, players.size());
gameBoard.initOfferingTiles(players.size());
gameBoard.setupInitialRows(players.size());
gameBoard.getTurnTile().setInitialOrder(players);
dealInitialFood();
round = 1;
totemPlacedCount = 0;
state = GameState.TOTEM_PLACEMENT;
logger.info("Game started! Round 1.");
logTurnOrder();
} catch (LoadingCardsException e) {
System.err.println("Fatal: could not load cards — " + e.getMessage());
state = GameState.GAME_OVER;
}
}
private void dealInitialFood() {
Player[] order = gameBoard.getTurnTile().getPositions();
for (int i = 0; i < order.length; i++) {
int food = (i == 0) ? 2 : (i <= 2) ? 3 : 4;
order[i].addFood(food);
logger.info(" " + order[i].getNickname() + " starts with " + food + " food.");
}
}
// =========================================================================
// PHASE 1 — TOTEM PLACEMENT
// =========================================================================
/**
* Places the player's totem on an offering tile.
*
* Players must place in the order defined by the TurnTile (top to bottom).
* If the wrong player calls this, it is rejected.
*
* @param player the player attempting to place
* @param tileIndex 0-based index into the offering tile list
* @return true if placement was accepted
*/
public boolean placeTotem(Player player, int tileIndex) {
if (state != GameState.TOTEM_PLACEMENT) {
logger.info("Error: not the totem-placement phase.");
return false;
}
// Enforce turn order
Player expected = gameBoard.getTurnTile().getPositions()[totemPlacedCount];
if (expected != player) {
logger.info("Error: it is " + expected.getNickname()
+ "'s turn to place, not " + player.getNickname() + ".");
return false;
}
List<OfferingTile> tiles = gameBoard.getOfferingTiles();
if (tileIndex < 0 || tileIndex >= tiles.size()) {
logger.info("Error: tile index " + tileIndex + " out of range.");
return false;
}
OfferingTile chosen = tiles.get(tileIndex);
if (!gameBoard.placeTotem(player, chosen, gameBoard.getTurnTile())) {
logger.info("Tile " + chosen.getLetter() + " is already occupied.");
return false;
}
totemPlacedCount++;
logger.info("player {} TOTEM PLACED ON TILE {}", player.getNickname() , chosen.getLetter());
// + " (" + totemPlacedCount + "/" + players.size() + ").");
if (totemPlacedCount == players.size()) {
logger.info("START ACTION RESOLUTION");
startActionResolution();
}
return true;
}
// =========================================================================
// PHASE 2 — ACTION RESOLUTION
// =========================================================================
private void startActionResolution() {
state = GameState.ACTION_RESOLUTION;
currentOfferingTileIndex = 0;
logger.info("All totems placed — resolving actions left to right.");
loadNextPlayerActions();
}
/**
* Scans offering tiles left to right for the next occupied one, loads that
* player's actions, and either resolves them automatically (food tile) or
* waits for player input (card tiles).
*
* Calls onAllActionsResolved() when every tile has been processed.
*/
private void loadNextPlayerActions() {
List<OfferingTile> tiles = gameBoard.getOfferingTiles();
while (currentOfferingTileIndex < tiles.size()) {
OfferingTile tile = tiles.get(currentOfferingTileIndex);
if (tile.isEmpty()) {
currentOfferingTileIndex++;
continue;
}
Player player = tile.getOccupant();
pendingActions = new ArrayList<>(tile.getActions());
logger.info(" PLAYER {} PENDING_ACTIONS {} ", player.getNickname() , pendingActions );
// --- Food tile: resolve entirely without player input ---
if (pendingActions.get(0) == Symbol.FOOD) {
int food = pendingActions.size();
player.addFood(food);
BuildingManager.bonusEndTurn(player, food); // BONUS_FOOD_ENDTURN building effect
logger.info(player.getNickname() + " receives " + food
+ " food from tile " + tile.getLetter() + ".");
pendingActions.clear();
finishPlayerTurn(player);
currentOfferingTileIndex++;
continue;
}
// --- Card tile: strip impossible actions first ---
filterImpossibleActions(player);
if (pendingActions.isEmpty()) {
// All actions were impossible (both rows empty)
finishPlayerTurn(player);
currentOfferingTileIndex++;
continue;
}
// Wait for the player to call resolveCardAction()
logger.info("player {} pending ACTIONS {}" , player.getNickname() , pendingActions);
// logger.info("It is " + player.getNickname() + "'s turn — actions: " + pendingActions);
return;
}
// Every tile has been processed
onAllActionsResolved();
}
/**
* Strips actions that cannot be executed because the required row is empty.
* Pure filter — no side effects beyond mutating pendingActions.
*/
private void filterImpossibleActions(Player player) {
Card ctop = gameBoard.getTopRow().stream().filter(s -> s instanceof CharacterCard || s instanceof BuildingCard).findFirst().orElse(null);
Card cdown = gameBoard.getBottomRow().stream().filter(s -> s instanceof CharacterCard || s instanceof BuildingCard).findFirst().orElse(null);
boolean topEmpty = ctop == null;
boolean bottomEmpty = cdown == null;
if (topEmpty) {
long removed = pendingActions.stream().filter(s -> s == Symbol.UP).count();
pendingActions.removeIf(s -> s == Symbol.UP);
if (removed > 0)
logger.info("{} LOSES ACTION UP" ,player.getNickname(), removed);
}
if (bottomEmpty) {
long removed = pendingActions.stream().filter(s -> s == Symbol.DOWN).count();
pendingActions.removeIf(s -> s == Symbol.DOWN);
if (removed > 0)
logger.info("{} LOSES ACTION DOWN" ,player.getNickname(), removed);
}
}
/**
* Resolves one card-pick action for the current player.
* Works for both CharacterCards and BuildingCards.
*
* For BuildingCards: builder discount is applied and the player must have
* enough food. If not, the card is put back and the call is rejected — the
* player must choose a different card.
*
* @param player the acting player (must match the current tile occupant)
* @param fromTop true = pick from the top row; false = from the bottom row
* @param cardId the ID of the card to take
* @return true if the action was accepted and consumed
*/
public ActionResult resolveCardAction(Player player, boolean fromTop, int cardId) {
if (state != GameState.ACTION_RESOLUTION) {
logger.info("Error: not in ACTION_RESOLUTION phase.");
return ActionResult.failure("Error: not in ACTION_RESOLUTION phase.");
}
OfferingTile currentTile = gameBoard.getOfferingTiles().get(currentOfferingTileIndex);
if (currentTile.getOccupant() != player) {
logger.info("Error: it is not " + player.getNickname() + "'s turn to act.");
return ActionResult.failure("Error: it is not " + player.getNickname() + "'s turn to act.");
}
Symbol required = fromTop ? Symbol.UP : Symbol.DOWN;
if (!pendingActions.contains(required)) {
logger.info("Error: no " + required + " action available for "
+ player.getNickname() + ".");
return ActionResult.failure("Error: no " + required + " action available for "
+ player.getNickname() + ".");
}
// Find the card in the correct row
Card card = fromTop
? gameBoard.takeFromTopRow(cardId)
: gameBoard.takeFromBottomRow(cardId);
if (card == null) {
logger.info("Error: card " + cardId + " not found in the "
+ (fromTop ? "top" : "bottom") + " row.");
return ActionResult.failure("Error: card " + cardId + " not found in the "
+ (fromTop ? "top" : "bottom") + " row.");
}
// Event cards can never be taken by players
if (card instanceof EventCard) {
putCardBack(card, fromTop);
logger.info("Error: Event cards cannot be taken.");
return ActionResult.failure("Error: Event cards cannot be taken.");
}
// Resolve the card
if (card instanceof BuildingCard) {
ActionResult result =resolveBuildingCard(player, (BuildingCard) card);
if (!result.isSuccess()) {
putCardBack(card, fromTop);
return result;
}
} else {
resolveCharacterCard(player, (CharacterCard) card);
}
// Consume the action
pendingActions.remove(required);
logger.info("player {} TOOK CARD {} --> PEDNING ACTIONS {}",player.getNickname(), cardId, pendingActions);
// A row may have become empty after this draw — re-check impossible actions
if (!pendingActions.isEmpty()) {
filterImpossibleActions(player);
}
// If no more actions remain, finish this player's turn
if (pendingActions.isEmpty()) {
finishPlayerTurn(player);
currentOfferingTileIndex++;
loadNextPlayerActions();
}
return ActionResult.ok();
}
/**
* Handles building card acquisition.
* Applies builder discount, checks affordability, deducts food, and
* registers the building with BuildingManager.
*
* @return true if the acquisition succeeded
*/
private ActionResult resolveBuildingCard(Player player, BuildingCard building) {
int discount = player.getPlayerTribe().buildersDiscount();
int actualCost = Math.max(0, building.getCost() - discount);
if (player.getFoodTokens() < actualCost) {
logger.info(player.getNickname() + " cannot afford building "
+ building.getCardId() + " (cost after discount: " + actualCost
+ ", has: " + player.getFoodTokens() + " food).");
return ActionResult.failure(player.getNickname() + " cannot afford building "
+ building.getCardId() + " (cost after discount: " + actualCost
+ ", has: " + player.getFoodTokens() + " food).");
}
player.removeFood(actualCost);
player.getPlayerTribe().addBuilding(building);
gameBoard.getBuildingManager().addActiveBuilding(building, player);
logger.info(player.getNickname() + " bought building "
+ building.getCardId() + " for " + actualCost + " food.");
return ActionResult.ok();
}
/**
* Handles character card acquisition and triggers all immediate effects:
*
* Hunter with leg icon (iconValue > 0):
* Take 1 food per Hunter currently in tribe (including this one).
*
* FOOD_FOR_SIX building:
* Take 6 food if this draw completes a full set of all 6 character types.
*
* FOOD_PER_INVENTORS building:
* Take 3 food if this Inventor forms a matching pair.
*
* The card is added to the tribe BEFORE effects fire so all counts include it.
*/
private void resolveCharacterCard(Player player, CharacterCard character) {
player.getPlayerTribe().addCharacter(character);
// Hunter with leg icon: immediate food reward
if (character.getCharacterType() == CharacterType.HUNTER
&& character.getIconValue() > 0) {
int food = player.getPlayerTribe().huntersNumber() * character.getIconValue();
player.addFood(food);
logger.info(player.getNickname() + "'s hunter (leg icon) grants +"
+ food + " food.");
}
// Building-triggered effects on character draw
gameBoard.getBuildingManager().foodForSix(player, character);
if (character.getCharacterType() == CharacterType.INVENTOR) {
gameBoard.getBuildingManager().foodPerInventors(player, character);
}
logger.info(player.getNickname() + " drew "
+ character.getCharacterType() + " (id " + character.getCardId() + ").");
}
/**
* Marks the current player as done with their tile actions.
* Calls TurnTile.returnTotem(), which places them in the next available
* slot and immediately applies the position food reward or penalty.
*/
private void finishPlayerTurn(Player player) {
int slot = gameBoard.getTurnTile().returnTotem(player);
OfferingTile off = gameBoard.getOfferingTile(player);
logger.info("player {} leaving offering {} " ,player.getNickname(), off);
if(off!=null) off.setOccupant(null);
logger.info("player {} left offering {} slot {} " ,player.getNickname(), off, slot);
}
// =========================================================================
// PHASE 2b — EXTRA DRAW (EXTRA_DRAW building effect)
// =========================================================================
/**
* Called when every offering tile has been resolved.
* Checks if any player owns the EXTRA_DRAW building and, if so, enters
* the EXTRA_DRAW phase. Otherwise proceeds directly to end of round.
*
* Rulebook appendix: "After resolving all actions (once all totems are back
* on the TurnTile) and before the end-of-round phase, you may take 1
* Character or Building card (paying its cost) from the top row."
*/
private void onAllActionsResolved() {
logger.info("All actions resolved.");
extraDrawQueue.clear();
// Iterate in turn order so extra draws happen in a consistent sequence
for (Player p : gameBoard.getTurnTile().getPositions()) {
if (p != null && gameBoard.getBuildingManager().extraDraw(p)) {
extraDrawQueue.add(p);
}
}
if (!extraDrawQueue.isEmpty()) {
state = GameState.EXTRA_DRAW;
System.out.println("Extra draw phase: "
+ extraDrawQueue.get(0).getNickname() + " may draw first.");
} else {
endRound();
}
}
/**
* The player uses their EXTRA_DRAW building to take one card from the top row.
* Building cards may also be taken, paying the cost after builder discounts.
*
* @param player the player taking the extra draw
* @param cardId the ID of the card they want from the top row
* @return true if the action was accepted
*/
public ActionResult extraDrawAction(Player player, int cardId) {
if (state != GameState.EXTRA_DRAW) {
System.out.println("Error: not in EXTRA_DRAW phase.");
return ActionResult.failure("Error: not in EXTRA_DRAW phase.");
}
if (extraDrawQueue.isEmpty() || extraDrawQueue.get(0) != player) {
System.out.println("Error: it is not " + player.getNickname()
+ "'s extra draw turn.");
return ActionResult.failure("Error: it is not " + player.getNickname()
+ "'s extra draw turn.");
}
Card card = gameBoard.takeFromTopRow(cardId);
if (card == null) {
System.out.println("Error: card " + cardId + " not found in top row.");
return ActionResult.failure("Error: card " + cardId + " not found in top row.");
}
if (card instanceof EventCard) {
gameBoard.getTopRow().add(card);
System.out.println("Error: cannot take Event cards.");
return ActionResult.failure("Error: cannot take Event cards.");
}
if (card instanceof BuildingCard) {
ActionResult result = resolveBuildingCard(player, (BuildingCard) card);
if (!result.isSuccess()) {
gameBoard.getTopRow().add(card);
return result;
}
} else {
resolveCharacterCard(player, (CharacterCard) card);
}
advanceExtraDrawQueue();
return ActionResult.ok();
}
/**
* The player declines to use their EXTRA_DRAW building this round.
*
* @return true if accepted
*/
public boolean skipExtraDraw(Player player) {
if (state != GameState.EXTRA_DRAW) return false;
if (extraDrawQueue.isEmpty() || extraDrawQueue.get(0) != player) return false;
System.out.println(player.getNickname() + " skips extra draw.");
advanceExtraDrawQueue();
return true;
}
private void advanceExtraDrawQueue() {
extraDrawQueue.remove(0);
if (extraDrawQueue.isEmpty()) {
endRound();
} else {
System.out.println("Next extra draw: " + extraDrawQueue.get(0).getNickname());
}
}
// =========================================================================
// END OF ROUND
// =========================================================================
/**
* Runs all end-of-round steps in rulebook order:
*
* Step 1 — Resolve Event cards.
* Normal rounds: only bottom row events.
* Round 10: events from BOTH rows; then GAME_OVER.
* Step 2 — Advance rows: discard bottom non-buildings, shift top
* non-buildings down, draw (numPlayers + 4) new cards.
* Step 3 — If a new Era card was revealed in step 2, trigger
* the Era transition (building row swap).
* Step 4 — Start the next round.
*/
private void endRound() {
state = GameState.EVENT_RESOLUTION;
//notify( GameState.EVENT_RESOLUTION);
logger.info("--- End of round " + round + " ---");
boolean isFinalRound = (round == TOTAL_ROUNDS);
// Step 1: resolve events
resolveVisibleEvents(isFinalRound);
if (isFinalRound) {
state = GameState.GAME_OVER;
notify( GameState.GAME_OVER, endGame());
logger.info("Round 10 complete — game over!");
return;
}
// Step 2: advance rows; returns true if a new Era card was revealed
boolean eraChangeTriggered = gameBoard.advanceRows(players.size());
// Step 3: handle Era transition if needed
if (eraChangeTriggered) {
gameBoard.triggerEraChange(players.size());
}
// Step 4: start next round
startNewRound();
}
/**
* Resolves all visible Event cards in the correct order.
*
* Rules:
* - Multiple events of different types: any order, Sustainment always last.
* - Two events of the same type in the same round: resolve in Era order.
* - Round 10: events from both rows must be resolved.
*
* @param bothRows true means events from both rows are included (round 10)
*/
private void resolveVisibleEvents(boolean bothRows) {
List<EventCard> events = bothRows
? gameBoard.getAllVisibleEvents() // already sorted by Era ordinal
: gameBoard.getVisibleEvents(); // already sorted by Era ordinal
if (events.isEmpty()) {
logger.info("No events to resolve this round.");
return;
}
// Sustainment must always be resolved last
List<EventCard> others = new ArrayList<>();
List<EventCard> sustainments = new ArrayList<>();
for (EventCard e : events) {
if (e.getEvent() == Event.SUSTAINMENT) sustainments.add(e);
else others.add(e);
}
for (EventCard e : others) resolveOneEvent(e);
for (EventCard e : sustainments) resolveOneEvent(e);
}
private void resolveOneEvent(EventCard event) {
notify( GameState.EVENT_RESOLUTION, event.toString());
logger.info("EVENT Resolving: " + event.getEvent() + " (Era " + event.getEra() + ")");
EventsSolver.solveEvents(Collections.singletonList(event), players);
}
private void startNewRound() {
round++;
notify( GameState.NEXT_ROUND, "Round="+ round);
logger.info("startNewRound: {}", round );
totemPlacedCount = 0;
currentOfferingTileIndex = 0;
pendingActions.clear();
gameBoard.clearOfferingTiles();
gameBoard.getTurnTile().resetTrack();
// notify( GameState.TOTEM_PLACEMENT);
state = GameState.TOTEM_PLACEMENT;
logger.info("=== NEW Round " + round + " ===");
logTurnOrder();
}
// =========================================================================
// END GAME
// =========================================================================
/**
* Computes the final leaderboard and returns it as a formatted string.
*
* Final score = prestige points accumulated during play
* + Tribe.endPoints() which covers: builder PP, inventor PP,
* artist PP, building base PP, and building end-game abilities.
*
* IMPORTANT: do NOT call BuildingManager.endgameForSix() /
* endgameBonusCharacter() / endgameBonusPoints() here.
* Tribe.endPoints() already computes those bonuses. Calling both would
* count them twice.
*
* Tie-breaking rule: most food wins. Further ties share the victory.
*/
public String endGame() {
state = GameState.GAME_OVER;
List<Player> ranked = new ArrayList<>(players);
ranked.sort((a, b) -> {
int diff = totalScore(b) - totalScore(a);
if (diff != 0) return diff;
return b.getFoodTokens() - a.getFoodTokens();
});
StringBuilder sb = new StringBuilder("=== FINAL LEADERBOARD ===\n");
for (int i = 0; i < ranked.size(); i++) {
Player p = ranked.get(i);
sb.append(i + 1).append(". ")
.append(p.getNickname())
.append("").append(totalScore(p)).append(" PP")
.append(" (food: ").append(p.getFoodTokens()).append(")\n");
}
Player winner = ranked.get(0);
sb.append("\nWINNER: ")
.append(winner.getNickname().toUpperCase())
.append(" with ").append(totalScore(winner)).append(" PP!");
return sb.toString();
}
/** Prestige points accumulated during play + final scoring from the Tribe engine. */
private int totalScore(Player p) {
return p.getPrestigePoints() + p.getPlayerTribe().endPoints();
}
// =========================================================================
// HELPERS
// =========================================================================
private void putCardBack(Card card, boolean wasTopRow) {
if (wasTopRow) gameBoard.getTopRow().add(card);
else gameBoard.getBottomRow().add(card);
}
private void logTurnOrder() {
Player[] order = gameBoard.getTurnTile().getPositions();
StringBuilder sb = new StringBuilder("Turn order: ");
for (int i = 0; i < order.length; i++) {
if (order[i] != null)
sb.append(i + 1).append(". ").append(order[i].getNickname()).append(" ");
}
logger.info("logTurnOrder: " + sb);
}
// =========================================================================
// GETTERS
// =========================================================================
public List<Player> getPlayers() { return Collections.unmodifiableList(players); }
public GameBoard getGameBoard() { return gameBoard; }
public int getRound() { return round; }
public GameState getState() { return state; }
public void setState(GameState s) { this.state = s; }
public List<Symbol> getPendingActions() { return Collections.unmodifiableList(pendingActions); }
/**
* Returns the player currently expected to act:
* TOTEM_PLACEMENT — the next player who should place their totem
* ACTION_RESOLUTION — the player currently resolving tile actions
* EXTRA_DRAW — the next player to make their extra-draw decision
*/
public Player getCurrentPlayer() {
switch (state) {
case TOTEM_PLACEMENT:
if (totemPlacedCount < players.size())
return gameBoard.getTurnTile().getPositions()[totemPlacedCount];
return null;
case ACTION_RESOLUTION:
List<OfferingTile> tiles = gameBoard.getOfferingTiles();
if (currentOfferingTileIndex < tiles.size())
return tiles.get(currentOfferingTileIndex).getOccupant();
return null;
case EXTRA_DRAW:
return extraDrawQueue.isEmpty() ? null : extraDrawQueue.get(0);
default:
return null;
}
}
@Override
public String toString() {
return "Game{" +
" state=" + state +
",\n round=" + round +
",\n totemPlacedCount=" + totemPlacedCount +
",\n currentOfferingTileIndex=" + currentOfferingTileIndex +
",\n pendingActions=" + pendingActions +
",\n extraDrawQueue=" + extraDrawQueue +
" \n GameBoard=" + gameBoard +
'}';
}
public record GameEventNotification(GameState event, String message) {}
public interface GameEventListener {
void onEvent(GameEventNotification notification);
}
// Campo privato
private GameEventListener eventListener;
// Setter per il controller FX
public void setEventListener(GameEventListener listener) {
this.eventListener = listener;
}
// Metodi privati di notifica
private void notify(GameState event, String message) {
if (eventListener != null)
eventListener.onEvent(new GameEventNotification(event, message));
}
private void notify(GameState event) {
notify(event, null);
}
}

View File

@@ -0,0 +1,68 @@
package Server.Automaton;
public enum GameState implements Comparable<GameState> {
UNKNOWN,
SETUP,
NEXT_ROUND,
TOTEM_PLACEMENT,
ACTION_RESOLUTION,
END_ACTION,
EXTRA_DRAW,
EVENT_RESOLUTION,
GAME_OVER;
GameState next(int turn){
switch (this) {
case SETUP:
return SETUP;
case NEXT_ROUND:
return NEXT_ROUND;
case TOTEM_PLACEMENT:
return ACTION_RESOLUTION;
case ACTION_RESOLUTION:
return END_ACTION;
case EXTRA_DRAW:
return EXTRA_DRAW;
case END_ACTION:
if(turn != 10) return GAME_OVER;
return TOTEM_PLACEMENT;
default: return UNKNOWN;
}
}
static GameState fromString(String s){
return switch (s.toUpperCase().charAt(0)) {
case 'S' -> SETUP;
case 'T' -> TOTEM_PLACEMENT;
case 'A' -> ACTION_RESOLUTION;
case 'E' -> END_ACTION;
case 'X' -> EXTRA_DRAW;
case 'G' -> GAME_OVER;
case 'N' -> NEXT_ROUND;
default -> UNKNOWN;
};
}
@Override
public String toString() {
switch (this) {
case SETUP:
return "SETUP";
case TOTEM_PLACEMENT:
return "TOTEM_PLACEMENT";
case ACTION_RESOLUTION:
return "ACTION_RESOLUTION";
case END_ACTION:
return "END_ACTION";
case EVENT_RESOLUTION:
return "EVENT_RESOLUTION";
case NEXT_ROUND:
return "NEXT_ROUND";
case EXTRA_DRAW:
return "EXTRA_DRAW";
case GAME_OVER:
return "GAME_OVER";
default: return "UNKNOWN";
}
}
}

View File

@@ -0,0 +1,214 @@
package Server;
import Server.Cards.BuildingCard;
import Server.Cards.CharacterCard;
import Server.Cards.CharacterType;
import Server.Cards.Trigger;
import Server.Utils.EventsManagerException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BuildingManager {
private static List<BuildingCard> activeBuildings;
public BuildingManager() {
activeBuildings = new ArrayList<>();
}
public void addActiveBuilding (BuildingCard buildingCard, Player player) {
buildingCard.setOwner(player);
activeBuildings.add(buildingCard);
}
public static BuildingCard playerHasBuilding(Player player, Trigger trigger) {
for (BuildingCard buildingCard : activeBuildings) {
if(buildingCard.getAbilityTrigger() == trigger && buildingCard.getOwner() == player) {
return buildingCard;
}
}
return null;
}
public static int numberOfTargets(Player player, List<BuildingCard> buildingCards) {
int numCard = 0;
for (BuildingCard buildingCard : buildingCards) {
List<CharacterCard> playerCharacters = player.getPlayerTribe().getCharacters();
for (CharacterCard card : playerCharacters) {
if (card.getCharacterType() == buildingCard.getEffectTarget()) {
numCard++;
}
}
}
return numCard;
}
public static List<BuildingCard> playerHasVariousBuildings(Player player, Trigger trigger) {
List<BuildingCard> buildingCards = new ArrayList<>();
for (BuildingCard buildingCard : activeBuildings) {
if(buildingCard.getAbilityTrigger() == trigger && buildingCard.getOwner() == player) {
buildingCards.add(buildingCard);
}
}
return buildingCards;
}
//When a player draw a characterCard, if said card complete a set of 6
//different character type, give the player 6 food
public static void foodForSix(Player player, CharacterCard characterCard) {
if(playerHasBuilding(player, Trigger.FOOD_FOR_SIX) == null){
return;
}
List<CharacterCard> playerTribe = player.getPlayerTribe().getCharacters();
int [] preDraw = new int[CharacterType.values().length];
int [] postDraw = new int[CharacterType.values().length];
for(CharacterCard card: playerTribe){
preDraw[card.getCharacterType().ordinal()]++;
postDraw[card.getCharacterType().ordinal()]++;
}
postDraw[characterCard.getCharacterType().ordinal()]++;
int minPre = Arrays.stream(preDraw).min().getAsInt();
int minPost = Arrays.stream(postDraw).min().getAsInt();
if(minPre < minPost){
player.addFood(6);
}
}
//the method return the discount received for the player based on the cards he owns
//the card providing the discount are indicated on the BuildingCard
public static int sustainDiscount(Player player) {
List<BuildingCard> buildingCards = playerHasVariousBuildings(player, Trigger.SUSTAIN_DISCOUNT);
if(buildingCards.isEmpty()){
return 0;
}
return numberOfTargets(player, buildingCards);
}
//if the player is going to lose during the shamanicRitual, the loss are nullif
public static boolean shamanNoLoss(Player player) {
return playerHasBuilding(player, Trigger.SHAMAN_NO_LOSS) != null;
}
//check if the player has a SHAMAN_DOUBLE_POINTS card
public static boolean shamanDoublePoints(Player player) {
return playerHasBuilding(player, Trigger.SHAMAN_DOUBLE_POINTS) != null;
}
//if the player has a BONUS_END_TURN buildingCard the player gain additional food
//after placing his totem on a tile whit food
public static int bonusEndTurn(Player player, int positionReward) {
if (playerHasBuilding(player, Trigger.BONUS_FOOD_ENDTURN) == null) return -1;
if (positionReward >= 1){
player.addFood(1);
return 1;
}
return 0;
}
//if the player has a BuildingCard whit FOOD_PER_INVENTORS, and he
//draws a copy of an inventor that is already in his tribe he gains 3 food
public static void foodPerInventors(Player player, CharacterCard characterCard) {
if(playerHasBuilding(player, Trigger.FOOD_PER_INVENTORS) == null){
return;
}
if(characterCard.getCharacterType()!= CharacterType.INVENTOR){
throw new EventsManagerException("Invalid character type");
}
List<CharacterCard> playerTribe = player.getPlayerTribe().getCharacters();
int numberOfInventors = 0;
for(CharacterCard card: playerTribe){
if(card.getIconValue() == characterCard.getIconValue() && card.getCharacterType() == CharacterType.INVENTOR){
numberOfInventors++;
}
}
if(numberOfInventors%2 == 1)
player.addFood(3);
}
//if the player has a SHAMAN_BONUS building the function return 3, else return 0
public static int shamanBonus(Player player) {
if (BuildingManager.playerHasBuilding(player, Trigger.SHAMAN_BONUS) == null) return 0;
return 3;
}
//return if HUNT_BONUS is active for the player
public static boolean hunterBonus(Player player) {
return playerHasBuilding(player, Trigger.HUNT_BONUS) != null;
}
//return if ENDGAME_BUILDER_BONUS is active for the player
public static boolean endgameBuilderBonus(Player player) {
return playerHasBuilding(player, Trigger.ENDGAME_BUILDER_BONUS) != null;
}
//return the amount of food gained if the player has a BuildingCard with
//PAINTING_FOOD_BONUS, a unit for each ARTIST in his tribe
public static void paintingFoodBonus(Player player) {
if(playerHasBuilding(player, Trigger.PAINTING_FOOD_BONUS) == null) return;
player.addFood(player.getPlayerTribe().artistsNumber());
}
//return the amount of buildingPoint if the player has a BuildingCard with
//ENDGAME_FOR_SIX, a unit for each set of 6 character of different Type
public static void endgameForSix(Player player) {
if(playerHasBuilding(player, Trigger.ENDGAME_FOR_SIX) == null) return;
List<CharacterCard> playerTribe = player.getPlayerTribe().getCharacters();
int [] typeDuplicates = new int[CharacterType.values().length];
for(CharacterCard card: playerTribe){
typeDuplicates[card.getCharacterType().ordinal()]++;
}
int minCharacterType = Arrays.stream(typeDuplicates).min().getAsInt();
player.addPrestigePoints(minCharacterType*6);
}
//add the bonus prestigePoints due to ENDGAME_BONUS_CHARACTER, the amount is based
//on the CharacterType indicated on the BuildingCards, 3 for each character of the same type
//A plater can own different BuildingCards with the same effect
public static void endgameBonusCharacter(Player player) {
List<BuildingCard> buildingCards = playerHasVariousBuildings(player, Trigger.ENDGAME_BONUS_CHARACTER);
if(buildingCards.isEmpty()){
return;
}
player.addPrestigePoints(numberOfTargets(player, buildingCards)* 3);
}
//return if EXTRA_DRAW is active for the player
public static boolean extraDraw(Player player) {
return playerHasBuilding(player, Trigger.EXTRA_DRAW) != null;
}
//return if the player has a BuildingCard with
//ENDGAME_BONUS_POINTS, then add 25 points if he has
public static void endgameBonusPoints(Player player) {
if(playerHasBuilding(player, Trigger.ENDGAME_BONUS_POINTS) == null) return;
player.addPrestigePoints(25);
}
}

View File

@@ -0,0 +1,48 @@
package Server;
import java.util.*;
public class BuildingRules {
private static final Map<Integer, Map<Era, Integer>> RULES = new HashMap<>();
static {
// 2 players
Map<Era, Integer> p2 = new EnumMap<>(Era.class);
p2.put(Era.I, 1);
p2.put(Era.II, 2);
p2.put(Era.III, 3);
// 3 players
Map<Era, Integer> p3 = new EnumMap<>(Era.class);
p3.put(Era.I, 2);
p3.put(Era.II, 2);
p3.put(Era.III, 4);
// 4 players
Map<Era, Integer> p4 = new EnumMap<>(Era.class);
p4.put(Era.I, 2);
p4.put(Era.II, 3);
p4.put(Era.III, 4);
// 5 players
Map<Era, Integer> p5 = new EnumMap<>(Era.class);
p4.put(Era.I, 2);
p4.put(Era.II, 3);
p4.put(Era.III, 5);
// insert into main map
RULES.put(2, p2);
RULES.put(3, p3);
RULES.put(4, p4);
RULES.put(5, p5);
}
public static int getBuildingCards(int players, Era era) {
Map<Era, Integer> eraMap = RULES.get(players);
if (eraMap!=null){
Integer value = eraMap.get(era);
if (value!=null) return value;
}
return 0;
}
}

View File

@@ -0,0 +1,84 @@
package Server.Cards;
import Server.Era;
import Server.Player;
public class BuildingCard extends Card{
private final int cost;
private final int endPP;
private final Trigger abilityTrigger;
private final CharacterType effectTarget;
private Player owner;
public BuildingCard(int cardId, int forMinPlayer, Era era, int cost, int endPP, Trigger abilityTrigger,CharacterType characterType, Player owner) {
super(cardId, forMinPlayer, era);
this.cost = cost;
this.endPP = endPP;
this.abilityTrigger = abilityTrigger;
this.effectTarget = characterType;
this.owner = owner;
}
public BuildingCard(int cardId, int forMinPlayer, Era era, int cost, int endPP, Trigger abilityTrigger, CharacterType characterType) {
super(cardId, forMinPlayer, era);
this.cost = cost;
this.endPP = endPP;
this.abilityTrigger = abilityTrigger;
this.effectTarget = characterType;
this.owner = null;
}
public BuildingCard(int cardId, int forMinPlayer, Era era, int cost, int endPP, Trigger abilityTrigger) {
super(cardId, forMinPlayer, era);
this.cost = cost;
this.endPP = endPP;
this.abilityTrigger = abilityTrigger;
this.effectTarget = null;
this.owner = null;
}
public void setOwner(Player owner) {
this.owner = owner;
}
public int getCost() {
return cost;
}
public int getEndPP() {
return endPP;
}
public Trigger getAbilityTrigger() {
return abilityTrigger;
}
public Player getOwner() {
return owner;
}
public CharacterType getEffectTarget() {
return effectTarget;
}
public static BuildingCard parsRow(String row) {
String cleanRow = row.trim();
String[] values = cleanRow.split(";");
int cardId = Integer.parseInt(values[1]);
int forMinPlayer = Integer.parseInt(values[2]);
Era era = Era.valueOf(values[3]);
int cost = Integer.parseInt(values[4]);
int endPP = Integer.parseInt(values[5]);
Trigger abilityTrigger = Trigger.valueOf(values[6]);
/* MANCA LA COLONNA 8 nel CSV
if(abilityTrigger == Trigger.SUSTAIN_DISCOUNT || abilityTrigger == Trigger.ENDGAME_BONUS_CHARACTER){
CharacterType effectTarget = CharacterType.valueOf(values[7]);
return new BuildingCard(cardId, forMinPlayer, era, cost, endPP, abilityTrigger, effectTarget);
}
*/
return new BuildingCard(cardId, forMinPlayer, era, cost, endPP, abilityTrigger);
}
}

View File

@@ -0,0 +1,35 @@
package Server.Cards;
import Server.Era;
public abstract class Card {
private final int cardId;
private final int forMinPlayer;
private final Era era;
public Card(int cardId, int forMinPlayer, Era era) {
this.cardId = cardId;
this.forMinPlayer = forMinPlayer;
this.era = era;
}
public int getCardId() {
return cardId;
}
public Era getEra() {
return era;
}
public int getForMinPlayer() {
return forMinPlayer;
}
@Override
public String toString() {
return "Card{" +
"cardId=" + cardId +
", era=" + era +
'}';
}
}

View File

@@ -0,0 +1,131 @@
package Server.Cards;
import Server.Automaton.Game;
import Server.Era;
import Server.Utils.LoadingCardsException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
public class CardDeck {
private List<Card> tribeDeck;
private Map<Era, List<Card>> buildingDeck;
private static final Logger logger = LogManager.getLogger(CardDeck.class);
public List<Card> getTribeDeck() {
return tribeDeck;
}
public List<Card> getBuildingDeck(Era era) {
return buildingDeck.get(era);
}
public void setForNPlayer(String path, int n) throws LoadingCardsException {
List<Card> tribe = new ArrayList<>();
List<Card> building = new ArrayList<>();
try {
List<String> rows = Files.readAllLines(Path.of(path));
int p=0;
for (String row : rows) {
String cleanRow = row.trim();
String[] fields = cleanRow.split(";");
logger.info((p++) + " ROW " +row);
switch (fields[0]) {
case "C":
tribe.add(CharacterCard.parsRow(row));
break;
case "E":
tribe.add(EventCard.parsRow(row));
break;
case "B":
building.add(BuildingCard.parsRow(row));
break;
default:
throw new LoadingCardsException("Content not supported");
}
}
} catch (IOException e) {
logger.error(e);
throw new LoadingCardsException("file not found");
}
building = CardDeck.shuffle(building);
this.tribeDeck = new ArrayList<>();
for(Card card : tribe) {
if (card.getForMinPlayer() <= n)
this.tribeDeck.add(card);
}
this.tribeDeck = CardDeck.shuffle(this.tribeDeck);
// groups the building cards by era
this.buildingDeck = building.stream().collect(Collectors.groupingBy(Card::getEra));
}
public List<Card> drawTribe(int n) {
List<Card> cards = new ArrayList<>();
for (int i = 0; i < n; i++) {
if(tribeDeck.isEmpty()) break;
cards.add(tribeDeck.getFirst());
tribeDeck.remove(tribeDeck.getFirst());
}
return cards;
}
public List<Card> drawBuilding(int n, Era era) {
List<Card> cards = new ArrayList<>();
for (int i = 0; i < n; i++) {
if(buildingDeck.isEmpty()) break;
Card card = buildingDeck.get(era).getFirst();
cards.add(card);
buildingDeck.remove(card);
}
return cards;
}
public static List<Card> shuffle(List<Card> cards) {
List<Card> shuffled = new ArrayList<>();
for(Era e: Era.values()) {
List<Card> cardsToShuffle = new ArrayList<>();
for(Card card : cards) {
if(card.getEra() == e){
cardsToShuffle.add(card);
}
}
Collections.shuffle(cardsToShuffle);
shuffled.addAll(cardsToShuffle);
}
return shuffled;
}
public Card getFirstCardForCover() {
if(tribeDeck.isEmpty()) return null;
Card card = tribeDeck.getFirst();
return card;
}
public Card drawTribeOne() {
if(tribeDeck.isEmpty()) return null;
Card card = tribeDeck.getFirst();
tribeDeck.remove(card);
return card;
}
public Card drawBuildingOne(Era era) {
if(buildingDeck.isEmpty()) return null;
Card card = buildingDeck.get(era).getFirst();
buildingDeck.get(era).remove(card);
return card;
}
}

View File

@@ -0,0 +1,58 @@
package Server.Cards;
import Server.Era;
import Server.Utils.LoadingCardsException;
public class CharacterCard extends Card{
private final CharacterType characterType;
private final int iconValue;
private final int prestigePoints;
public CharacterCard(int cardId, int forMinPlayer, Era era, CharacterType characterType, int iconValue, int prestigePoints) {
super(cardId, forMinPlayer, era);
this.characterType = characterType;
this.iconValue = iconValue;
this.prestigePoints = prestigePoints;
}
public CharacterType getCharacterType() {
return characterType;
}
public int getIconValue() {
return iconValue;
}
public int getPrestigePoints() {
return prestigePoints;
}
public static CharacterCard parsRow(String row){
String cleanRow = row.trim();
String[] values = cleanRow.split(";");
if(!values[0].equals("C")){
throw new LoadingCardsException("Not a character card");
}
int cardId = Integer.parseInt(values[1]);
int forMinPlayer = Integer.parseInt(values[2]);
Era era = Era.valueOf(values[3]);
CharacterType characterType = CharacterType.valueOf(values[4]);
int iconValue = Integer.parseInt(values[5]);
int prestigePoints = Integer.parseInt(values[6]);
return new CharacterCard(cardId, forMinPlayer, era, characterType, iconValue, prestigePoints);
}
@Override
public String toString() {
return "CharacterCard{" +
"characterType=" + characterType +
", value=" + iconValue +
", points=" + prestigePoints +
'}';
}
}

View File

@@ -0,0 +1,10 @@
package Server.Cards;
public enum CharacterType {
INVENTOR,
HUNTER,
GATHERER,
SHAMAN,
ARTIST,
BUILDER
}

View File

@@ -0,0 +1,8 @@
package Server.Cards;
public enum Event {
SUSTAINMENT,
HUNT,
SHAMANIC_RITUAL,
CAVE_PAINTINGS
}

View File

@@ -0,0 +1,55 @@
package Server.Cards;
import Server.Era;
import Server.Utils.LoadingCardsException;
public class EventCard extends Card {
private final Event event;
private final int firstValue;
private final int secondValue;
public EventCard(int cardId, int forMinPlayer, Era era, Event event, int firstValue, int secondValue) {
super(cardId, forMinPlayer, era);
this.event = event;
this.firstValue = firstValue;
this.secondValue = secondValue;
}
public Event getEvent() {
return event;
}
public int getFirstValue() {
return firstValue;
}
public int getSecondValue() {
return secondValue;
}
public static EventCard parsRow(String row){
String cleanRow = row.trim();
String[] values = cleanRow.split(";");
if (!values[0].equals("E")) {
throw new LoadingCardsException("Not an EventCard");
}
int cardId = Integer.parseInt(values[1]);
int forMinPlayer = Integer.parseInt(values[2]);
Era era = Era.valueOf(values[3]);
Event event = Event.valueOf(values[4]);
int firstValue = Integer.parseInt(values[5]);
int secondValue = Integer.parseInt(values[6]);
return new EventCard(cardId, forMinPlayer, era, event, firstValue, secondValue);
}
@Override
public String toString() {
return "EventCard{" +
"event=" + event +
", firstValue=" + firstValue +
", secondValue=" + secondValue +
'}';
}
}

View File

@@ -0,0 +1,18 @@
package Server.Cards;
public enum Trigger {
FOOD_FOR_SIX,
SUSTAIN_DISCOUNT,
SHAMAN_NO_LOSS,
BONUS_FOOD_ENDTURN,
FOOD_PER_INVENTORS,
SHAMAN_BONUS,
SHAMAN_DOUBLE_POINTS,
HUNT_BONUS,
ENDGAME_BUILDER_BONUS,
PAINTING_FOOD_BONUS,
ENDGAME_FOR_SIX,
ENDGAME_BONUS_CHARACTER,
EXTRA_DRAW,
ENDGAME_BONUS_POINTS
}

View File

@@ -0,0 +1,684 @@
package Server;
import Server.Automaton.ActionResult;
import Server.Automaton.Game;
import Server.Cards.*;
import Server.Utils.GameException;
import javafx.animation.*;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.util.Duration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
public class DeckGridAppFX extends Application {
private static final Logger logger = LogManager.getLogger(DeckGridAppFX.class);
private static final int IMG_HEIGHT=200;
private PDDocument documentFront;
private PDDocument documentBack;
private PDFRenderer pdfRendererFront;
private PDFRenderer pdfRendererBack;
private int totalCards = 0;
// Contenitori Layout
private HBox topMenu;
private HBox topRow;
private HBox centerRow;
private HBox bottomRow;
private HBox playersArea; // NUOVO: Area per i giocatori
private Button btnRefresh;
private Button btnTop;
private Button btnBottom;
private Button btnChooseOffering;
private Button btnMsg;
// --------------------------------
@Override
public void start(Stage primaryStage) {
List<Player> players = new ArrayList<>();
//players.add(new Player("Yellow", TotemColor.YELLOW));
players.add(new Player("Blue", TotemColor.BLUE));
//players.add(new Player("Purple", TotemColor.PURPLE));
players.add(new Player("Red", TotemColor.RED));
//players.add(new Player("Green", TotemColor.GREEN));
Game game = new Game(players);
String fileCards="/home/lorenzo/dev/Mesos2/src/main/resources/files/cards.csv";
String fileCardsImgFront="/home/lorenzo/dev/Mesos2/src/main/resources/files/Cards_total_front_PROMO.pdf";
String fileCardsImgCover="/home/lorenzo/dev/Mesos2/src/main/resources/files/Cards_total_back_PROMO.pdf";
try {
File fileFront = new File(fileCardsImgFront);
File fileBack = new File(fileCardsImgCover);
documentFront = PDDocument.load(fileFront);
pdfRendererFront = new PDFRenderer(documentFront);
documentBack = PDDocument.load(fileBack);
pdfRendererBack = new PDFRenderer(documentBack);
} catch ( IOException e) {
throw new RuntimeException(e);
}
game.newGame(fileCards);
game.setEventListener(notification -> {
Platform.runLater(() -> {
drawGameState(game);
switch (notification.event()) {
case TOTEM_PLACEMENT-> showPopup("TOTEM PLACEMENT\n" + (notification.message()!=null?notification.message():""));
case SETUP -> showPopup("SETUP \n" + (notification.message()!=null?notification.message():""));
case ACTION_RESOLUTION -> showPopup("ACTION_RESOLUTION \n" + (notification.message()!=null?notification.message():"" ));
case END_ACTION -> showPopup("END_ACTION \n" + (notification.message()!=null?notification.message():"" ));
case EVENT_RESOLUTION -> showPopup("EVENT_RESOLUTION \n" + (notification.message()!=null?notification.message():"" ));
case EXTRA_DRAW -> showPopup("EXTRA_DRAW \n" + (notification.message()!=null?notification.message():"" ));
case GAME_OVER -> showPopup("GAME OVER");
}
});
});
primaryStage.setTitle("Tavolo da Gioco - Board & Players");
btnTop = new Button("Pick Top");
btnBottom = new Button("Pick Bottom");
btnMsg = new Button("Game State");
btnRefresh= new Button("Refresh");
btnChooseOffering= new Button("Choose Offering");
//btnNext.setDisable(true);
topMenu = new HBox(15, btnRefresh, btnMsg);
topMenu.setAlignment(Pos.CENTER);
topMenu.setPadding(new Insets(10));
Label stateLabel = new Label("Game State = " + game.getState() );
stateLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
stateLabel.setTextFill(Color.DARKBLUE);
//showPopup(topMenu, "STARTING GAME");
btnMsg.setOnAction(e -> {
new Alert(Alert.AlertType.INFORMATION,
game.toString()
).showAndWait();
});
btnRefresh.setOnAction(e -> {
drawGameState(game);
});
topRow = createRowContainer();
centerRow = createRowContainer();
bottomRow = createRowContainer();
// NUOVO: Inizializza l'area giocatori
playersArea = new HBox(30);
playersArea.setAlignment(Pos.CENTER);
playersArea.setPadding(new Insets(20));
VBox tableArea = new VBox(20,
new Label(" "), topRow,
new Label(" "), centerRow,
new Label(" "), bottomRow,
new Label("--- ASSET PLAYERS ---"), playersArea
);
tableArea.setAlignment(Pos.CENTER);
tableArea.setPadding(new Insets(20));
ScrollPane scrollPane = new ScrollPane(tableArea);
scrollPane.setFitToWidth(true);
BorderPane root = new BorderPane();
root.setTop(topMenu);
root.setCenter(scrollPane);
Scene scene = new Scene(root, 1300, 900);
primaryStage.setScene(scene);
primaryStage.show();
drawGameState(game);
}
private HBox createRowContainer() {
HBox row = new HBox(10);
row.setAlignment(Pos.CENTER);
return row;
}
private void drawGameState(Game game) {
if (documentFront == null) return;
topMenu.getChildren().clear();
topRow.getChildren().clear();
centerRow.getChildren().clear();
bottomRow.getChildren().clear();
playersArea.getChildren().clear();
drawTopMenu(topMenu, game);
drawTopRow(topRow, game);
drawTurnTileAndOffering(centerRow, game.getPlayers().size(), game);
drawBottomRow(bottomRow, game );
List<Player> players =game.getPlayers();
renderPlayers(players, game);
logger.info(game.getCurrentPlayer());
}
private void pickTopCardAction(Player player, GameBoard board, int cardid){
Card card = board.getTopRow().stream()
.filter(CharacterCard.class::isInstance)
.map(CharacterCard.class::cast)
.filter(c -> c.getCardId() == cardid)
.findFirst()
.orElse(null);
if (card != null) {
board.getTopRow().remove(card);
CharacterCard charCard = (CharacterCard)card;
player.addCharacterToTribe((CharacterCard) card);
}
}
private void pickBottomCardAction(Player player, GameBoard board, int cardid){
Card card = board.getBottomRow().stream()
.filter(CharacterCard.class::isInstance)
.map(CharacterCard.class::cast)
.filter(c -> c.getCardId() == cardid)
.findFirst()
.orElse(null);
if (card != null) {
board.getBottomRow().remove(card);
player.addCharacterToTribe((CharacterCard) card);
}
}
private void drawBottomRow(HBox row, Game game) {
for (Card c : game.getGameBoard().getBottomRow()) {
ImageView cardImage = createCardImageFromPdf(c.getCardId(), IMG_HEIGHT, true);
if (cardImage != null) {
// 1. Create a Label for your debugging text
Label debugLabel = new Label("ID: " + c.getCardId());
// 2. Style the label so it's readable over any card background
// (White text with a semi-transparent black background)
debugLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.7); " +
"-fx-text-fill: white; " +
"-fx-padding: 3px; " +
"-fx-font-weight: bold;");
// 3. Create a StackPane and add both the image and the text
StackPane cardContainer = new StackPane();
cardContainer.getChildren().addAll(cardImage, debugLabel);
// 4. Align the text wherever you want (e.g., Top-Left corner of the card)
StackPane.setAlignment(debugLabel, Pos.TOP_LEFT);
// You can also use Pos.CENTER, Pos.BOTTOM_RIGHT, etc.
cardImage.setOnMouseClicked(event -> {
logger.info("Bottom Card clicked");
Player p = game.getCurrentPlayer();
//pickBottomCardAction(p, game.getGameBoard(), c.getCardId());
ActionResult result = game.resolveCardAction(p, false, c.getCardId());
if (!result.isSuccess()) {
new Alert(Alert.AlertType.ERROR,
result.getErrorMessage()
).showAndWait();
}
drawGameState(game);
});
// 5. Add the StackPane (which now holds both) to the row
row.getChildren().add(cardContainer);
}
}
}
private void drawTopMenu(HBox row, Game game) {
//topMenu = new HBox(15, btnChooseOffering, btnAction, btnTop, btnBottom, btnMsg);
//topMenu.setAlignment(Pos.CENTER);
//topMenu.setPadding(new Insets(10));
Label stateLabel = new Label("Game State = " + game.getState() );
stateLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
stateLabel.setTextFill(Color.DARKBLUE);
row.getChildren().add(stateLabel);
row.getChildren().add(btnRefresh);
row.getChildren().add(btnMsg);
}
private void drawTopRow(HBox row, Game game) {
for (Card c : game.getGameBoard().getTopRow()) {
ImageView cardImage = createCardImageFromPdf(c.getCardId(), IMG_HEIGHT, true);
if (cardImage != null) {
// 1. Create a Label for your debugging text
Label debugLabel = new Label("ID: " + c.getCardId());
// 2. Style the label so it's readable over any card background
// (White text with a semi-transparent black background)
debugLabel.setStyle("-fx-background-color: rgba(0, 0, 0, 0.7); " +
"-fx-text-fill: white; " +
"-fx-padding: 3px; " +
"-fx-font-weight: bold;");
// 3. Create a StackPane and add both the image and the text
StackPane cardContainer = new StackPane();
cardContainer.getChildren().addAll(cardImage, debugLabel);
// 4. Align the text wherever you want (e.g., Top-Left corner of the card)
StackPane.setAlignment(debugLabel, Pos.TOP_LEFT);
// You can also use Pos.CENTER, Pos.BOTTOM_RIGHT, etc.
cardImage.setOnMouseClicked(event -> {
logger.info("Card clicked");
Player p = game.getCurrentPlayer();
//pickTopCardAction(p, game.getGameBoard(), c.getCardId());
//game.resolveCardAction(p, true, c.getCardId());
ActionResult result = game.resolveCardAction(p, true, c.getCardId());
if (!result.isSuccess()) {
new Alert(Alert.AlertType.ERROR,
result.getErrorMessage()
).showAndWait();
}
drawGameState(game);
});
// 5. Add the StackPane (which now holds both) to the row
row.getChildren().add(cardContainer);
}
}
}
private StackPane drawTurnTileCardImageWithPlayers(String imagePath, int height, Player[] players) {
try {
Image fxImage = new Image("file:" + imagePath);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
StackPane stack = new StackPane(imageView);
VBox playerBox = new VBox(5);
playerBox.setPadding(new Insets(5));
playerBox.setAlignment(Pos.CENTER_RIGHT);
// 🔥 DEBUG background (to SEE the box)
playerBox.setStyle("-fx-background-color: rgba(255,0,0,0.3);");
for (Player p : players) {
if (p==null) continue;
Rectangle rect = new Rectangle(15,15);
rect.setFill(p.getTotemColor().getFxColor());
rect.setStroke(Color.WHITE);
rect.setStrokeWidth(1);
playerBox.getChildren().add(rect);
}
playerBox.setPadding(new Insets(22));
// 🔥 IMPORTANT: bring to front
playerBox.toFront();
// position it clearly
StackPane.setAlignment(playerBox, Pos.CENTER);
stack.getChildren().add(playerBox);
return stack;
} catch (Exception e) {
logger.error("Errore caricamento immagine {}", imagePath, e);
return null;
}
}
private void drawTurnTileAndOffering(HBox row, int n, Game game) {
// Clear row if needed (optional but recommended)
row.getChildren().clear();
// --- LEFT BLOCK (Turn + initial card) ---
VBox turnBox = new VBox(5);
turnBox.setAlignment(Pos.CENTER);
// Turn label
int round = game.getRound(); // adjust if different
Label turnLabel = new Label("Round = " + round);
turnLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
turnLabel.setTextFill(Color.DARKBLUE);
Card first = game.getGameBoard().getCardDeck().getFirstCardForCover();
if (first!=null){
ImageView imgCover = createCardImageFromPdf(first.getCardId(), 120, false);
turnBox.getChildren().addAll(turnLabel, imgCover);
} else turnBox.getChildren().addAll(turnLabel);
// Add FIRST
row.getChildren().add(turnBox);
// --- TURN TILE ---
StackPane pane = drawTurnTileCardImageWithPlayers(
"/home/lorenzo/dev/Mesos2/src/main/resources/files/Start_" + n + "P.png",
IMG_HEIGHT,
game.getGameBoard().getTurnTile().getPositions()
);
row.getChildren().add(pane);
// --- OFFERINGS ---
for (OfferingTile offering : game.getGameBoard().getOfferingTiles()) {
Player occupant = offering.getOccupant();
StackPane offPane =drawOfferingCardImageWithTotem(
"/home/lorenzo/dev/Mesos2/src/main/resources/files/offering" + offering.getLetter() + ".png",
IMG_HEIGHT,
occupant, game, offering);
if (offPane != null) {
row.getChildren().add(offPane);
}
}
}
// --- NUOVO: RENDERIZZAZIONE GIOCATORI E RAGGRUPPAMENTO (JAVA 8) ---
private void renderPlayers(List<Player> players, Game game) {
playersArea.getChildren().clear();
for (Player player : players) {
boolean active = player.equals(game.getCurrentPlayer());
VBox playerBox = new VBox(10);
playerBox.setPadding(new Insets(15));
playerBox.setStyle("-fx-border-color: #555; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-color: #f9f9f9; -fx-background-radius: 10;");
if (active) {
// Strong highlight (gold border + soft background)
playerBox.setStyle(
"-fx-border-color: gold;" +
"-fx-border-width: 3;" +
"-fx-border-radius: 10;" +
"-fx-background-color: linear-gradient(to bottom, #fffbe6, #f9f9f9);" +
"-fx-background-radius: 10;"
);
// Slight scale-up
playerBox.setScaleX(1.05);
playerBox.setScaleY(1.05);
// Glow effect
DropShadow glow = new DropShadow();
glow.setColor(Color.GOLD);
glow.setRadius(20);
playerBox.setEffect(glow);
// Smooth pulse animation (NOT blinking)
Timeline pulse = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(glow.radiusProperty(), 10)
),
new KeyFrame(Duration.seconds(1.2),
new KeyValue(glow.radiusProperty(), 25)
)
);
pulse.setAutoReverse(true);
pulse.setCycleCount(Animation.INDEFINITE);
pulse.play();
}
HBox nameRow = new HBox(8);
nameRow.setAlignment(Pos.CENTER_LEFT);
Rectangle rect = new Rectangle(12, 12);
TotemColor color = player.getTotemColor();
rect.setFill(color.getFxColor());
rect.setStroke(Color.BLACK);
rect.setStrokeWidth(1);
Label nameLbl = new Label(player.getNickname());
nameLbl.setFont(Font.font("System", FontWeight.BOLD, 16));
if (active) {
Label turnLbl = new Label(" ▶ Your Turn");
turnLbl.setTextFill(Color.GOLDENROD);
turnLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
nameRow.getChildren().addAll(rect, nameLbl, turnLbl);
} else {
nameRow.getChildren().addAll(rect, nameLbl);
}
// Risorse Cibo e Soldi
Label statsLbl = new Label("🍖 Food: " + player.getFoodTokens() + " | 💰 Points: " + player.getPrestigePoints());
statsLbl.setTextFill(Color.DARKRED);
statsLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
playerBox.getChildren().addAll(nameRow, statsLbl);
Map<CharacterType, List<CharacterCard>> groupedCards = player.getPlayerTribe().getCharacters().stream()
.collect(Collectors.groupingBy(CharacterCard::getCharacterType));
// Itera sui gruppi creati e genera la grafica
groupedCards.forEach((type, cardsOfType) -> {
Label typeLbl = new Label("Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
HBox cardImagesRow = new HBox(5);
for (Card c : cardsOfType) {
// Carte più piccole (90px) per l'area giocatore
ImageView img = createCardImageFromPdf(c.getCardId(), 90, true);
if (img != null) cardImagesRow.getChildren().add(img);
}
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
});
Map<Era, List<BuildingCard>> groupedBuildCards = player.getPlayerTribe().getBuildingCard().stream()
.collect(Collectors.groupingBy(BuildingCard::getEra));
groupedBuildCards.forEach((type, cardsOfType) -> {
Label typeLbl = new Label("Build Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
HBox cardImagesRow = new HBox(5);
for (Card c : cardsOfType) {
// Carte più piccole (90px) per l'area giocatore
ImageView img = createCardImageFromPdf(c.getCardId(), 90, true);
if (img != null) cardImagesRow.getChildren().add(img);
}
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
//playerBox.getChildren().addAll(nameRow, statsLbl);
});
playersArea.getChildren().add(playerBox);
}
}
private ImageView createCardImageFromPdf(int pageIndex, int height, boolean front) {
try {
BufferedImage bim =null;
if (front) bim = pdfRendererFront.renderImageWithDPI(pageIndex-1, 100);
else bim = pdfRendererBack.renderImageWithDPI(pageIndex-1, 100);
Image fxImage = SwingFXUtils.toFXImage(bim, null);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
return imageView;
} catch (IOException e) {
logger.error("Errore rendering pagina {}", pageIndex, e);
return null;
}
}
private StackPane drawOfferingCardImageWithTotem(String imagePath, int height, Player occupant, Game game, OfferingTile offering) {
try {
Image fxImage = new Image("file:" + imagePath);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
imageView.setOnMouseClicked(event -> {
int idx = game.getGameBoard().getOfferingTiles().indexOf(offering);
game.placeTotem(game.getCurrentPlayer(), idx);
logger.info(" PLAYER {} choose {} ", game.getCurrentPlayer().getNickname() , offering );
drawGameState(game);
});
StackPane stack = new StackPane(imageView);
if(occupant!=null){
Rectangle rect = new Rectangle(25, 25);
rect.setFill(occupant.getTotemColor().getFxColor());
rect.setStroke(Color.WHITE);
rect.setStrokeWidth(1);
StackPane.setAlignment(rect, Pos.TOP_CENTER);
rect.setTranslateY(20);
StackPane.setMargin(rect, new Insets(5));
stack.getChildren().add(rect);
}
return stack;
} catch (Exception e) {
logger.error("Errore caricamento immagine {}", imagePath, e);
return null;
}
}
private ImageView createCardImage(String imagePath, int height) {
try {
Image fxImage = new Image("file:" + imagePath);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
return imageView;
} catch (Exception e) {
logger.error("Errore caricamento immagine {}", imagePath, e);
return null;
}
}
private void showPopup(String message) {
Platform.runLater(() -> {
Label label = new Label("" + message); // icon improves readability
label.setWrapText(true);
label.setMaxWidth(400);
label.setStyle(
"-fx-background-color: rgba(20,20,20,0.9);" +
"-fx-text-fill: white;" +
"-fx-font-size: 20px;" +
"-fx-font-weight: bold;" +
"-fx-padding: 15 25 15 25;" +
"-fx-background-radius: 12;" +
"-fx-border-radius: 12;" +
"-fx-border-color: #00c3ff;"
);
label.setOpacity(0); // start invisible
topMenu.getChildren().add(label);
// Fade IN
FadeTransition fadeIn = new FadeTransition(Duration.millis(250), label);
fadeIn.setFromValue(0);
fadeIn.setToValue(1);
// Stay visible
PauseTransition pause = new PauseTransition(Duration.seconds(4));
// Fade OUT
FadeTransition fadeOut = new FadeTransition(Duration.millis(400), label);
fadeOut.setFromValue(1.0);
fadeOut.setToValue(0.0);
fadeOut.setOnFinished(f -> topMenu.getChildren().remove(label));
fadeIn.play();
fadeIn.setOnFinished(e -> pause.play());
pause.setOnFinished(e -> fadeOut.play());
});
}
private void showPopupNo(String message) {
Platform.runLater(() -> {
Label label = new Label(message);
label.setStyle(
"-fx-background-color: rgba(0,0,0,0.75);" +
"-fx-text-fill: white;" +
"-fx-font-size: 28px;" +
"-fx-font-weight: bold;" +
"-fx-padding: 20 40 20 40;" +
"-fx-background-radius: 12;"
);
label.setMouseTransparent(true);
topMenu.getChildren().add(label);
PauseTransition pause = new PauseTransition(Duration.seconds(5));
pause.setOnFinished(e -> {
FadeTransition fade = new FadeTransition(Duration.millis(400), label);
fade.setFromValue(1.0);
fade.setToValue(0.0);
fade.setOnFinished(f -> topMenu.getChildren().remove(label));
fade.play();
});
pause.play();
});
}
@Override
public void stop() throws Exception {
if (documentFront != null) documentFront.close();
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}

View File

@@ -0,0 +1,19 @@
package Server;
public enum Era {
I,
II,
III,
FINAL;
public Era next() {
Era[] eras = values();
// Calcola l'indice del prossimo elemento
int nextOrdinal = this.ordinal() + 1;
if (nextOrdinal < eras.length) {
return eras[nextOrdinal];
}
return this;
}
}

View File

@@ -0,0 +1,136 @@
package Server;
import Server.Cards.Event;
import Server.Cards.EventCard;
import Server.Cards.Trigger;
import Server.Utils.EventsManagerException;
import java.util.ArrayList;
import java.util.List;
public class EventsSolver {
public static boolean solveEvents(List<EventCard> events, List<Player> players){
for(EventCard event: events){
switch (event.getEvent()){
case Event.SUSTAINMENT:
EventsSolver.sustainment(event, players);
break;
case Event.HUNT:
EventsSolver.hunt(event, players);
break;
case Event.SHAMANIC_RITUAL:
EventsSolver.shamanicRitual(event, players);
break;
case Event.CAVE_PAINTINGS:
EventsSolver.cavePaintings(event, players);
break;
default:
throw new EventsManagerException("Unknown event type");
}
}
return true;
}
public static List<Integer> sustainment(EventCard event, List<Player> players){
if(event.getEvent() != Event.SUSTAINMENT){throw new EventsManagerException("Not a sustainment card");}
List<Integer> result = new ArrayList<>();
for(Player p: players){
int subpoints = p.getPlayerTribe().getCharacters().size();
int discount = p.getPlayerTribe().gathererDiscount() + BuildingManager.sustainDiscount(p);
if(subpoints <= discount){subpoints = 0;}else{
subpoints -= discount;
}
if(p.getFoodTokens() >= subpoints){
p.removeFood(subpoints);
}else{
subpoints -= p.getFoodTokens();
p.removeFood(p.getFoodTokens());
p.removePrestigePoints(subpoints*event.getFirstValue());
}
result.add(p.getFoodTokens());
result.add(p.getPrestigePoints());
}
return result;
}
public static List<Integer> hunt(EventCard event, List<Player> players){
if(event.getEvent() != Event.HUNT){throw new EventsManagerException("Not a hunt card");}
List<Integer> result = new ArrayList<>();
for(Player p: players){
p.addFood(p.getPlayerTribe().huntersNumber());
p.addPrestigePoints(p.getPlayerTribe().huntersNumber()*event.getFirstValue());
if(BuildingManager.hunterBonus(p)){
p.addFood(p.getPlayerTribe().huntersNumber());
p.addPrestigePoints(p.getPlayerTribe().huntersNumber());
}
result.add(p.getFoodTokens());
result.add(p.getPrestigePoints());
}
return result;
}
public static List<Integer> shamanicRitual(EventCard event, List<Player> players){
if(event.getEvent() != Event.SHAMANIC_RITUAL){throw new EventsManagerException("Not a shamanic ritual card");}
List<Integer> result = new ArrayList<>();
int maxSymbols = 0;
int minSymbols = 999;
for(Player p: players){
int symbols = p.getPlayerTribe().shamansIcons() + BuildingManager.shamanBonus(p);
if (symbols > maxSymbols){maxSymbols = p.getPlayerTribe().shamansIcons();}
if (symbols < minSymbols){minSymbols = p.getPlayerTribe().shamansIcons();}
}
for(Player p: players){
if(p.getPlayerTribe().shamansIcons() == maxSymbols){
p.addPrestigePoints(event.getFirstValue());
//activating building shamanDoublePoints card effect
if(BuildingManager.shamanDoublePoints(p))
p.addPrestigePoints(event.getFirstValue());
}
if(p.getPlayerTribe().shamansIcons() == minSymbols){
//activating building shamanNoLoss card effect
if(!BuildingManager.shamanNoLoss(p))
p.removePrestigePoints(event.getSecondValue());
}
result.add(p.getPrestigePoints());
}
return result;
}
public static List<Integer> cavePaintings(EventCard event, List<Player> players){
if(event.getEvent() != Event.CAVE_PAINTINGS){throw new EventsManagerException("Not a cave painting card");}
List<Integer> result = new ArrayList<>();
for(Player p: players){
BuildingManager.paintingFoodBonus(p);
if(p.getPlayerTribe().artistsNumber() >= event.getFirstValue()){
p.addPrestigePoints(p.getPlayerTribe().artistsNumber()*event.getSecondValue());
}else{
p.removePrestigePoints(event.getSecondValue());
}
result.add(p.getPrestigePoints());
}
return result;
}
}

View File

@@ -0,0 +1,395 @@
package Server;
import Server.Automaton.Game;
import Server.Cards.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* Represents the physical game board: the two card rows, the offering tiles,
* the turn-order tile, and the card deck.
*
* Responsibilities:
* - Owning and mutating the top and bottom card rows.
* - Owning the OfferingTiles and the TurnTile.
* - Performing end-of-round row transitions and era-change building swaps.
* - Exposing visible EventCards to Game for resolution.
*
* NOT responsible for:
* - Resolving events (that is EventsSolver's job, called by Game).
* - Applying player rewards/penalties (that is Game's job).
* - Holding a reference to the player list.
*/
public class GameBoard {
private static final Logger logger = LogManager.getLogger(GameBoard.class);
// -------------------------------------------------------------------------
// Fields
// -------------------------------------------------------------------------
private Era era;
private final CardDeck cardDeck;
private final List<Card> topRow;
private final List<Card> bottomRow;
private final List<OfferingTile> offeringTiles;
private final TurnTile turnTile;
private final BuildingManager buildingManager;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
/**
* Creates the board for a new game.
* Call {@link #setupInitialRows(int)} and {@link #initOfferingTiles(int)}
* separately after construction (mirrors the rulebook setup steps).
*/
public GameBoard(Era startingEra, CardDeck cardDeck, int numPlayers) {
this.era = startingEra;
this.cardDeck = cardDeck;
this.topRow = new ArrayList<>();
this.bottomRow = new ArrayList<>();
this.offeringTiles = new ArrayList<>();
this.turnTile = new TurnTile(numPlayers);
this.buildingManager = new BuildingManager();
}
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
/**
* Creates and places the correct OfferingTiles for the given player count.
*
* Tile layout (from the rulebook):
* orderId 0 → tile A: 3 food (5-player only)
* orderId 1 → tile B: 1 DOWN (all)
* orderId 2 → tile C: 1 UP (all)
* orderId 3 → tile D: 2 DOWN (3+ players)
* orderId 4 → tile E: 1 DOWN 1 UP (all)
* orderId 5 → tile F: 2 UP (all)
* orderId 6 → tile G: 1 DOWN 2 UP (4+ players)
*
* With n players, exactly n tiles are used, starting from tile A if
* 5 players, otherwise starting from tile B.
*/
public void initOfferingTiles(int numPlayers) {
offeringTiles.clear();
switch (numPlayers) {
case 2:
offeringTiles.add(new OfferingTile(1));
offeringTiles.add(new OfferingTile(2));
offeringTiles.add(new OfferingTile(4));
offeringTiles.add(new OfferingTile(5));
break;
case 3:
offeringTiles.add(new OfferingTile(1));
offeringTiles.add(new OfferingTile(2));
offeringTiles.add(new OfferingTile(3));
offeringTiles.add(new OfferingTile(4));
offeringTiles.add(new OfferingTile(5));
break;
case 4:
offeringTiles.add(new OfferingTile(1));
offeringTiles.add(new OfferingTile(2));
offeringTiles.add(new OfferingTile(3));
offeringTiles.add(new OfferingTile(4));
offeringTiles.add(new OfferingTile(5));
offeringTiles.add(new OfferingTile(6));
break;
case 5:
offeringTiles.add(new OfferingTile(0));
offeringTiles.add(new OfferingTile(1));
offeringTiles.add(new OfferingTile(2));
offeringTiles.add(new OfferingTile(3));
offeringTiles.add(new OfferingTile(4));
offeringTiles.add(new OfferingTile(5));
offeringTiles.add(new OfferingTile(6));
break;
}
}
// Find the oddertingTile selected by Player
public OfferingTile getOfferingTile(Player p){
OfferingTile offering = offeringTiles.stream().filter(o -> p.equals(o.getOccupant())).findFirst().orElse(null);
return offering;
}
/**
* Draws the initial two rows of cards according to rulebook setup steps 4-5:
*
* Bottom row: draw cards one at a time until (numPlayers + 1) Character cards
* have been placed. Any Event card drawn is placed in the top row instead.
* Top row: fill to (numPlayers + 4) with additional draws (accounting for
* any events already placed there from the bottom-row draw).
*/
public void setupInitialRows(int numPlayers) {
topRow.clear();
bottomRow.clear();
// Draw bottom row — events are bumped to top row
while (bottomRow.size() < numPlayers + 1) {
Card card = cardDeck.drawTribeOne();
if (card instanceof EventCard) {
topRow.add(card);
logger.info("Setup: Event card " + card.getCardId() + " moved to top row.");
} else {
bottomRow.add(card);
}
}
// Fill top row up to numPlayers + 4
int needed = (numPlayers + 4) - topRow.size();
if (needed > 0) {
topRow.addAll(cardDeck.drawTribe(needed));
}
// Add all Building Era.I on top row 2P 1 3-4-5 2
topRow.add(cardDeck.drawBuildingOne(Era.I));
if (numPlayers > 2){
topRow.add(cardDeck.drawBuildingOne(Era.I));
}
}
// -------------------------------------------------------------------------
// End-of-round row management (rulebook "Fine del Round" steps 2-4)
// -------------------------------------------------------------------------
/**
* Performs the three row-management steps at the end of every round:
*
* Step 2 — Discard all Character and Event cards from the bottom row.
* Building cards stay.
* Step 3 — Move all Character and Event cards from the top row down to
* the bottom row. Building cards stay in the top row.
* Step 4 — Draw (numPlayers + 4) new cards into the top row.
*
* @return true if the newly drawn cards contain a card from the next Era,
* signalling that {@link #triggerEraChange()} should be called.
*/
public boolean advanceRows(int numPlayers) {
Era eraBeforeDraw = this.era;
// Step 2: discard non-building cards from the bottom row
bottomRow.removeIf(
c -> !(c instanceof BuildingCard)
);
// Step 3: move non-building cards from top row down to bottom row
Iterator<Card> it = topRow.iterator();
while (it.hasNext()) {
Card c = it.next();
if (!(c instanceof BuildingCard)) {
logger.debug("move card from top to bottom " + c);
bottomRow.add(c);
it.remove();
}
}
// Step 4: draw fresh cards into the top row
List<Card> newCards = cardDeck.drawTribe(numPlayers + 4);
topRow.addAll(newCards);
logger.info("NEW CARDS ON TOP {} " , newCards);
return isNewEraRevealed(eraBeforeDraw, newCards);
}
/**
* Returns true if any of the newly drawn cards belongs to an era
* that is strictly later than the current one.
*/
private boolean isNewEraRevealed(Era currentEra, List<Card> newCards) {
for (Card c : newCards) {
Era cardEra = eraOf(c);
if (cardEra != null && cardEra.ordinal() > currentEra.ordinal()) {
logger.info("FOUND NEW ERA {} " , cardEra);
return true;
}
}
return false;
}
// -------------------------------------------------------------------------
// Era change (rulebook "Inizio della Nuova Era")
// -------------------------------------------------------------------------
/**
* Advances the era and performs the three building-row transitions:
*
* Step 1 — (Era III only) Discard all Building cards from the bottom row.
* Step 2 — Move all Building cards from the top row to the bottom row
* (to the right of the Tribe cards). [Era II and III]
* Step 3 — Place the new era's Building cards face-up in the top row.
* [Era II and III]
*
* @return the new Era after the transition
*/
public Era triggerEraChange(int numPlayers) {
era = era.next(); // Era.I → II → III → FINAL
logger.info("ERA CHANGED → {} ", era);
// Step 1 (Era III only): remove old era buildings from the bottom row
if (era == Era.III) {
bottomRow.removeIf(c -> c instanceof BuildingCard);
}
// Step 2: move buildings from top row to bottom row
List<BuildingCard> descending = new ArrayList<>();
Iterator<Card> it = topRow.iterator();
while (it.hasNext()) {
Card c = it.next();
if (c instanceof BuildingCard) {
descending.add((BuildingCard) c);
it.remove();
}
}
bottomRow.addAll(descending);
logger.info("event=MOVED_BUILING era={} count={} cards={}", era, descending.size(), descending);
// Step 3: place the new era's building cards in the top row
int n = BuildingRules.getBuildingCards(numPlayers, era);
List<Card> newEraBuildings = cardDeck.drawBuilding(n , era);
topRow.addAll(newEraBuildings);
logger.info("event=ADD_BUILDINGS era={} count={} cards={}", era, n, newEraBuildings);
return era;
}
// -------------------------------------------------------------------------
// Offering tiles
// -------------------------------------------------------------------------
/**
* Attempts to place a player's totem on an offering tile.
*
* @return true if successful; false if the tile was already occupied
*/
public boolean placeTotem(Player player, OfferingTile tile, TurnTile turntile) {
if (!tile.isEmpty()) return false;
tile.setOccupant(player);
turntile.leaveTurnTile(player);
return true;
}
/**
* Removes all totems from offering tiles. Call at the start of each new round.
*/
public void clearOfferingTiles() {
for (OfferingTile tile : offeringTiles) {
tile.removeOccupant();
}
}
// -------------------------------------------------------------------------
// Card removal (called by Game when a player takes a card)
// -------------------------------------------------------------------------
/**
* Removes a card from the top row by ID and returns it.
*
* @return the card, or null if no card with that ID exists in the top row
*/
public Card takeFromTopRow(int cardId) {
return takeFromRow(topRow, cardId);
}
/**
* Removes a card from the bottom row by ID and returns it.
*
* @return the card, or null if no card with that ID exists in the bottom row
*/
public Card takeFromBottomRow(int cardId) {
return takeFromRow(bottomRow, cardId);
}
private Card takeFromRow(List<Card> row, int cardId) {
Iterator<Card> it = row.iterator();
while (it.hasNext()) {
Card c = it.next();
if (c.getCardId() == cardId) {
it.remove();
return c;
}
}
return null;
}
// -------------------------------------------------------------------------
// Event visibility
// -------------------------------------------------------------------------
/**
* Returns all EventCards currently in the bottom row, sorted by era
* (ascending) so they are resolved in the correct order.
* Sustainment is NOT sorted last here — that is Game's responsibility.
*/
public List<EventCard> getVisibleEvents() {
List<EventCard> events = new ArrayList<>();
for (Card c : bottomRow) {
if (c instanceof EventCard) {
events.add((EventCard) c);
}
}
events.sort((a, b) -> a.getEra().ordinal() - b.getEra().ordinal());
return events;
}
/**
* Returns EventCards from BOTH rows.
* Used during the final round when all visible events must be resolved.
*/
public List<EventCard> getAllVisibleEvents() {
List<EventCard> events = new ArrayList<>();
for (Card c : bottomRow) {
if (c instanceof EventCard) events.add((EventCard) c);
}
for (Card c : topRow) {
if (c instanceof EventCard) events.add((EventCard) c);
}
events.sort((a, b) -> a.getEra().ordinal() - b.getEra().ordinal());
return events;
}
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
/** Extracts the Era from a card, returning null for building cards. */
private Era eraOf(Card c) {
if (c instanceof CharacterCard) return ((CharacterCard) c).getEra();
if (c instanceof EventCard) return ((EventCard) c).getEra();
return null;
}
// -------------------------------------------------------------------------
// Getters
// -------------------------------------------------------------------------
public Era getEra() { return era; }
public List<Card> getTopRow() { return topRow; }
public List<Card> getBottomRow() { return bottomRow; }
public List<OfferingTile> getOfferingTiles() { return offeringTiles; }
public TurnTile getTurnTile() { return turnTile; }
public BuildingManager getBuildingManager() { return buildingManager; }
public CardDeck getCardDeck() { return cardDeck; }
@Override
public String toString() {
return "GameBoard{" +
"era=" + era +
",\n offeringTiles=" + offeringTiles +
",\n turnTile=" + turnTile +
'}';
}
}

View File

@@ -0,0 +1,148 @@
package Server;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
/**
* Represents a single tile on the offering track.
*
* Each tile has a fixed letter (AG) and a fixed list of actions the
* occupying player must resolve. The tile is either empty or occupied
* by exactly one player's totem at any given time.
*
* Tile layout (from the rulebook):
*
* Letter │ orderId │ Actions │ Player count
* ───────┼─────────┼──────────────────────┼─────────────
* A │ 0 │ FOOD FOOD FOOD │ 5 only
* B │ 1 │ BOTTOM │ all
* C │ 2 │ UP │ all
* D │ 3 │ BOTTOM BOTTOM │ 3+
* E │ 4 │ BOTTOM UP │ all
* F │ 5 │ UP UP │ all
* G │ 6 │ BOTTOM UP UP │ 4+
*
* Which tiles are included in a game is decided by GameBoard.initOfferingTiles(),
* not by this class.
*/
public class OfferingTile {
private static final char FIRST_LETTER = 'A';
private final int orderId;
private final char letter;
private final List<Symbol> actions; // unmodifiable after construction
private Player occupant;
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
public OfferingTile(int orderId) {
this.orderId = orderId;
this.letter = (char) (FIRST_LETTER + orderId);
this.occupant = null;
this.actions = Collections.unmodifiableList(buildActions(orderId));
}
/**
* Returns the fixed action list for each tile orderId.
* Extracted into a private method to keep the constructor clean.
*/
private static List<Symbol> buildActions(int orderId) {
List<Symbol> list = new ArrayList<>();
switch (orderId) {
case 0: // A — food tile (5p only)
list.add(Symbol.FOOD);
list.add(Symbol.FOOD);
list.add(Symbol.FOOD);
break;
case 1: // B
list.add(Symbol.DOWN);
break;
case 2: // C
list.add(Symbol.UP);
break;
case 3: // D
list.add(Symbol.DOWN);
list.add(Symbol.DOWN);
break;
case 4: // E
list.add(Symbol.DOWN);
list.add(Symbol.UP);
break;
case 5: // F
list.add(Symbol.UP);
list.add(Symbol.UP);
break;
case 6: // G
list.add(Symbol.DOWN);
list.add(Symbol.UP);
list.add(Symbol.UP);
break;
default:
throw new IllegalArgumentException("Invalid OfferingTile orderId: " + orderId);
}
return list;
}
// -------------------------------------------------------------------------
// Occupant management
// -------------------------------------------------------------------------
/** Returns true if no totem is currently placed on this tile. */
public boolean isEmpty() {
return occupant == null;
}
/**
* Places a player's totem on this tile.
* Callers should check {@link #isEmpty()} before calling this.
*/
public void setOccupant(Player player) {
this.occupant = player;
}
/**
* Removes the totem from this tile and returns the player who was on it,
* or null if the tile was already empty.
*/
public Player removeOccupant() {
Player previous = this.occupant;
this.occupant = null;
return previous;
}
// -------------------------------------------------------------------------
// Getters
// -------------------------------------------------------------------------
/** The player currently occupying this tile, or null if empty. */
public Player getOccupant() {
return occupant;
}
/**
* The actions this tile grants to its occupant (e.g. [BOTTOM, UP]).
* The returned list is unmodifiable.
*/
public List<Symbol> getActions() {
return actions;
}
/** 0-based position index; determines the tile's letter and action set. */
public int getOrderId() {
return orderId;
}
/** The tile's letter label ('A''G'), useful for display and logging. */
public char getLetter() {
return letter;
}
@Override
public String toString() {
return "Tile " + letter + " " + actions + (isEmpty() ? " [empty]" : " [" + occupant.getNickname() + "]");
}
}

View File

@@ -0,0 +1,96 @@
package Server;
import Server.Cards.CharacterCard;
import java.util.Objects;
public class Player {
//Attributes
private String nickname;
private TotemColor totemColor;
private int foodTokens;
private int prestigePoints;
private Tribe playerTribe;
//Constructor
public Player(String nickname, TotemColor totemColor) {
this.nickname = nickname;
this.totemColor = totemColor;
this.foodTokens = 0;
this.prestigePoints = 0;
this.playerTribe = new Tribe();
}
//Methods
public void addFood(int food){
this.foodTokens += food;
}
public boolean removeFood(int food){
if (this.foodTokens >= food) {
this.foodTokens -= food;
return true; // Pagamento andato a buon fine
}
return false; // Il giocatore non ha abbastanza cibo, transazione negata, la quantità di cibo rimane quella di prima.
}
public void addPrestigePoints(int prestige){
this.prestigePoints += prestige;
}
public int removePrestigePoints(int prestige){
this.prestigePoints -= prestige;
return prestigePoints;
}
public void addCharacterToTribe(CharacterCard card) {
int foodGained = this.playerTribe.addCharacter(card);
this.addFood(foodGained);
}
//Getters
public String getNickname() {
return nickname;
}
public int getFoodTokens() {
return foodTokens;
}
public int getPrestigePoints() {
return prestigePoints;
}
public TotemColor getTotemColor() {
return totemColor;
}
public Tribe getPlayerTribe() {
return playerTribe;
}
// two Player objects to be considered equal only by nickname
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Player player = (Player) o;
return Objects.equals(nickname, player.nickname);
}
@Override
public int hashCode() {
return Objects.hash(nickname);
}
@Override
public String toString() {
return "Player(" +
"name='" + nickname + '\'' +
", food=" + foodTokens +
", pp=" + prestigePoints +
')';
}
}

View File

@@ -0,0 +1,7 @@
package Server;
public enum Symbol {
UP,
DOWN,
FOOD
}

View File

@@ -0,0 +1,21 @@
package Server;
import javafx.scene.paint.Color;
public enum TotemColor {
RED(Color.RED),
BLUE(Color.BLUE),
YELLOW(Color.YELLOW),
GREEN(Color.GREEN),
PURPLE(Color.PURPLE);
private final Color fxColor;
TotemColor(Color fxColor) {
this.fxColor = fxColor;
}
public Color getFxColor() {
return fxColor;
}
}

View File

@@ -0,0 +1,239 @@
package Server;
import Server.Cards.BuildingCard;
import Server.Cards.CharacterCard;
import Server.Cards.CharacterType;
import Server.Cards.Trigger;
import java.util.ArrayList;
import java.util.List;
public class Tribe {
// Attributes
private List<CharacterCard> characters;
private List<BuildingCard> buildings;
// Constructor
public Tribe() {
this.characters = new ArrayList<>();
this.buildings = new ArrayList<>();
}
public List<CharacterCard> getCharacters() {
return characters;
}
public List<BuildingCard> getBuildingCard() {
return buildings;
}
// METODI
public int addCharacter(CharacterCard card) {
this.characters.add(card);
if (card.getCharacterType().equals(CharacterType.HUNTER)) {
return hunterGetFood(card);
}
return 0;
}
// Metodo per aggiungere un nuovo building alla tribù
public void addBuilding(BuildingCard card) {
this.buildings.add(card);
}
// Metodo per ottenere lo sconto in cibo in base a quanti gatherer abbiamo nella tribù
public int gathererDiscount() {
int discount = 0;
for (CharacterCard c : characters) {
if (c.getCharacterType() == CharacterType.GATHERER) {
discount += 3; // i gatherers prendono sempre 3 cibi
}
}
return discount;
}
// Metodo per ottenere lo sconto totale sugli edifici grazie ai builder nella tribù
public int buildersDiscount() {
int discount = 0;
for (CharacterCard c : characters) {
if (c.getCharacterType() == CharacterType.BUILDER) {
discount += c.getIconValue(); // con getIconValue intendo lo sconto del costruttore
}
}
return discount;
}
// Metodo che conta quante stelle degli sciamani abbiamo in totale nella tribù
public int shamansIcons() {
int totalIcons = 0;
for (CharacterCard c : characters) {
if (c.getCharacterType() == CharacterType.SHAMAN) {
totalIcons += c.getIconValue();
}
}
return totalIcons;
}
// Metodo che restituisce il numero di artisti nella tribù
public int artistsNumber() {
return countCharactersByType(CharacterType.ARTIST);
}
// Metodo che restituisce il numero di cacciatori nella tribù
public int huntersNumber() {
return countCharactersByType(CharacterType.HUNTER);
}
// Metodo universale per contare le carte di un certo tipo all'interno della tribù
public int countCharactersByType(CharacterType typeToCount) {
int count = 0;
for (CharacterCard c : characters) {
if (c.getCharacterType() == typeToCount) {
count++;
}
}
return count;
}
// Metodo che ritorna il numero totale di cibi ottenuti dopo aver pescato il cacciatore col cosciotto
public int hunterGetFood(CharacterCard hunter) {
return huntersNumber() * hunter.getIconValue(); // getIconValue = 1 se il cacciatore ha il cosciotto
}
// Metodo che restituisce i punti finali degli inventori
public int inventorsEndPoints() {
int inventorCount = 0;
List<Integer> uniqueInventions = new ArrayList<>();
for (CharacterCard c : characters) {
if (c.getCharacterType() == CharacterType.INVENTOR) {
inventorCount++;
int inventionId = c.getIconValue(); // Usiamo l'iconValue del file cards.csv
// Se non abbiamo ancora contato questa invenzione, la aggiungiamo
if (!uniqueInventions.contains(inventionId)) {
uniqueInventions.add(inventionId);
}
}
}
return inventorCount * uniqueInventions.size();
}
// Metodo che restituisce i punti finali degli artisti
public int artistsEndPoints() {
return (artistsNumber() / 2) * 10; // in java 1/2 fa 0
}
// Metodo che restituisce i punti finali dei costruttori
public int buildersEndPoints() {
int points = 0;
for (CharacterCard c : characters) {
if (c.getCharacterType() == CharacterType.BUILDER) {
points += c.getPrestigePoints();
}
}
return points;
}
// Metodo che restituisce i punti finali guadagnati grazie agli EFFETTI delle carte building
private int buildingAbilitiesEndPoints() {
int bonusPoints = 0;
for (BuildingCard b : buildings) {
Trigger trigger = b.getAbilityTrigger(); // leggiamo il trigger della carta edificio
// Se la carta per qualche motivo non ha trigger, passiamo alla prossima
if (trigger == null) {
continue;
}
switch (trigger) {
case ENDGAME_BUILDER_BONUS: // id carta: 107
bonusPoints += (buildersEndPoints() * 2);
break;
case ENDGAME_FOR_SIX: // id carta: 109
// Prepariamo i contatori per tutti e 6 i tipi di characters
int inv = 0, hun = 0, gat = 0, sha = 0, art = 0, bui = 0;
// contiamo le carte nella tribù
for (CharacterCard c : characters) {
switch (c.getCharacterType()) {
case INVENTOR: inv++; break;
case HUNTER: hun++; break;
case GATHERER: gat++; break;
case SHAMAN: sha++; break;
case ARTIST: art++; break;
case BUILDER: bui++; break;
}
}
// troviamo il numero di set completi
int min1 = Math.min(inv, hun);
int min2 = Math.min(gat, sha);
int min3 = Math.min(art, bui);
int completeSets = Math.min(Math.min(min1, min2), min3);
// aggiungiamo 6 punti prestigio per ogni set completo
bonusPoints += (completeSets * 6);
break;
case ENDGAME_BONUS_CHARACTER:
int id = b.getCardId(); // uso l'id della carta per capire che edificio è
// il numero id corrisponde al numero di pagina nel pdf
if (id == 110) { // edificio dei cacciatori
bonusPoints += countCharactersByType(CharacterType.HUNTER) * 3;
}
else if (id == 111) { // edificio dei gatherer
bonusPoints += countCharactersByType(CharacterType.GATHERER) * 4;
}
else if (id == 112) { // edificio degli sciamani
bonusPoints += countCharactersByType(CharacterType.SHAMAN) * 4;
}
else if (id == 113) { // edificio dei costruttori
bonusPoints += countCharactersByType(CharacterType.BUILDER) * 4;
}
else if (id == 114) { // edificio degli artisti
bonusPoints += countCharactersByType(CharacterType.ARTIST) * 4;
}
else if (id == 115) { // edificio degli inventori
bonusPoints += countCharactersByType(CharacterType.INVENTOR) * 2;
}
break;
case ENDGAME_BONUS_POINTS: // id carta: 117
bonusPoints += 25; // dà 25 punti bonus
break;
default:
break;
}
}
return bonusPoints;
}
// Metodo che calcola i punti finali totali
public int endPoints() {
int total = 0;
// sommiamo i punti calcolati dai vari personaggi
total += inventorsEndPoints();
total += artistsEndPoints();
total += buildersEndPoints();
// sommiamo i punti prestigio BASE di tutti gli edifici (EndPP)
for (BuildingCard b : buildings) {
total += b.getEndPP();
}
// sommiamo gli effetti degli edifici sul punteggio finale, NON quelli durante la partita
total += buildingAbilitiesEndPoints();
return total;
}
}

View File

@@ -0,0 +1,176 @@
package Server;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Represents the Turn Order tile.
*
* Lifecycle per round:
* 1. At round start, read positions top-to-bottom via nextToPlace() to know
* which player places their totem on the offering track first.
* 2. After a player resolves all their offering actions, they call returnTotem().
* returnTotem() places them in the next free slot (top-to-bottom) and
* immediately gives them the position reward/penalty.
* 3. The order in which players called returnTotem() becomes the new turn
* order for the NEXT round.
* 4. resetTrack() is called at the start of each new round to restart the
* placement cursor and the return-slot counter, WITHOUT clearing positions
* (they already hold the new order from step 2-3).
*/
public class TurnTile {
private static final Logger logger = LogManager.getLogger(TurnTile.class);
private final Player[] positions; // current turn order; updated each round via returnTotem()
private int nextFreeSlot; // next available slot for a returning totem
private int placementCursor; // cursor used during the totem-placement phase
public TurnTile(int numP) {
this.positions = new Player[numP];
this.nextFreeSlot = 0;
this.placementCursor = 0;
}
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
/**
* Randomises the initial turn order. Call once during game setup.
*/
public void setInitialOrder(List<Player> players) {
List<Player> shuffled = new ArrayList<>(players);
Collections.shuffle(shuffled);
for (int i = 0; i < shuffled.size(); i++) {
positions[i] = shuffled.get(i);
}
logger.info("setInitialOrder " + this);
}
// -------------------------------------------------------------------------
// Totem-placement phase (phase 1 of a round)
// -------------------------------------------------------------------------
/**
* Returns the next player who must place their totem on the offering track
* (following the current turn order, top to bottom).
* Returns null when all players have already been served this round.
*/
public Player nextToPlace() {
if (placementCursor < positions.length) {
return positions[placementCursor++];
}
return null;
}
/**
* Returns the player who is currently expected to place their totem
* (i.e. the last player returned by nextToPlace), or null if none yet.
*/
public Player getLastPlacedPlayer() {
if (placementCursor > 0) {
return positions[placementCursor - 1];
}
return null;
}
// -------------------------------------------------------------------------
// Action-resolution phase (phase 2 of a round)
// -------------------------------------------------------------------------
/**
* Called when a player has finished resolving all their offering actions.
* Places the player's totem in the next free slot (determining next-round
* order) and applies the food reward or penalty for that slot.
*
* Reward rules (from the rulebook):
* - Slot 0 (first to return): +1 food (2p), +2 food (3-4p), +3 food (5p)
* - Slot 1 (second to return, only 4-5 player games): +1 food
* - Last slot: pay 1 food; if unable, lose 2 PP instead
*
* @return the slot index the player was placed in
*/
public int returnTotem(Player player) {
logger.info("returnTotem " + player);
int slot = nextFreeSlot;
// --- Position food reward ---
int positionFood = 0;
if (slot == 0) {
positionFood = (positions.length == 2) ? 1
: (positions.length == 5) ? 3
: 2; // 3 or 4 players
} else if (slot == 1 && positions.length >= 4) {
positionFood = 1;
}
player.addFood(positionFood);
// --- Activate BONUS_FOOD_ENDTURN building if the player has it ---
// (The building gives +1 extra food whenever the player lands on a food slot)
BuildingManager.bonusEndTurn(player, positionFood);
// --- Last-slot penalty ---
if (slot == positions.length - 1) {
if (!player.removeFood(1)) {
player.removePrestigePoints(2);
}
}
positions[slot] = player;
nextFreeSlot++;
logger.info(player.getNickname() + " returned totem to slot " + slot
+ (positionFood > 0 ? " and received " + positionFood + " food" : ""));
return slot;
}
public void leaveTurnTile(Player player) {
for(int i=0;i<positions.length;i++)
if (positions[i] !=null && positions[i].equals(player)) positions[i]=null;
}
// -------------------------------------------------------------------------
// Round reset
// -------------------------------------------------------------------------
/**
* Resets the cursors for a new round.
* Does NOT clear positions: those already contain the new turn order
* established during the previous round's returnTotem() calls.
*/
public void resetTrack() {
nextFreeSlot = 0;
placementCursor = 0;
}
// -------------------------------------------------------------------------
// Getters
// -------------------------------------------------------------------------
/**
* Returns the full positions array (turn order).
* Index 0 = first to act, last index = last to act.
*/
public Player[] getPositions() {
return positions;
}
/** How many players have already returned their totem this round. */
public int getReturnedCount() {
return nextFreeSlot;
}
@Override
public String toString() {
return "TurnTile{" +
"positions=" + Arrays.toString(positions) +
'}';
}
}

View File

@@ -0,0 +1,89 @@
package Server;
import java.util.List;
public class TurnTileOld {
private final Player[] positions;
private int place;
private int currentplayer;
public TurnTileOld(int numP) {
this.positions = new Player[numP];
this.place = 0;
this.currentplayer = 0;
}
public Player[] startOrder (List<Player> players){
for (Player p: players){
positions[place]=p;
place++;
}
//Collections.shuffle(Arrays.asList(positions));
return positions;
}
public Player giveReward (Player player){
if (player == positions[positions.length-1]){
if (!player.removeFood(1)){
player.removePrestigePoints(2);
}
}
else if (player == positions[0]){
if (positions.length==2){
player.addFood(1);
}
else if (positions.length == 5){
player.addFood(3);
}
else{
player.addFood(2);
}
}
else if (player == positions[1] && positions.length >= 4){
player.addFood(1);
}
return player;
}
public Player nextPlayer() {
currentplayer++;
if (currentplayer== positions.length){
currentplayer=0;
}
return positions[currentplayer];
}
public Player addPlayer(Player player){
positions[currentplayer]=player;
return positions[currentplayer];
}
}

View File

@@ -0,0 +1,7 @@
package Server.Utils;
public class EventsManagerException extends RuntimeException {
public EventsManagerException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,7 @@
package Server.Utils;
public class GameException extends RuntimeException {
public GameException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,7 @@
package Server.Utils;
public class LoadingCardsException extends RuntimeException {
public LoadingCardsException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,18 @@
module org.example.mesosll07 {
requires javafx.controls;
requires javafx.fxml;
requires org.controlsfx.controls;
requires javafx.swing;
requires org.apache.pdfbox;
requires org.apache.logging.log4j;
opens org.example.mesosll07 to javafx.fxml;
exports org.example.mesosll07;
exports Server;
opens Server to javafx.fxml;
exports Server.Cards;
opens Server.Cards to javafx.fxml;
exports Server.Automaton;
opens Server.Automaton to javafx.fxml;
}

View File

@@ -0,0 +1,208 @@
package org.example.mesosll07;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DeckGridApp extends Application {
// Inizializza il Logger
private static final Logger logger = LogManager.getLogger(DeckGridApp.class);
private PDDocument document;
private PDFRenderer pdfRenderer;
private int totalCards = 0;
// Contenitori per le tre righe
private HBox topRow;
private HBox centerRow;
private HBox bottomRow;
private Button btnShuffle;
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Tavolo da Gioco - Carte Casuali");
logger.info("Avvio dell'applicazione JavaFX");
// Pulsanti
Button btnLoad = new Button("Carica PDF Mazzo");
btnShuffle = new Button("Rimescola Carte");
btnShuffle.setDisable(true); // Disabilitato finché non si carica un PDF
btnLoad.setOnAction(e -> loadPdf(primaryStage));
btnShuffle.setOnAction(e -> distributeRandomCards());
HBox topMenu = new HBox(15, btnLoad, btnShuffle);
topMenu.setAlignment(Pos.CENTER);
topMenu.setPadding(new Insets(10));
// Setup delle tre righe (spazio tra le carte impostato a 10px)
topRow = createRowContainer();
centerRow = createRowContainer();
bottomRow = createRowContainer();
// Contenitore verticale per le righe
VBox tableArea = new VBox(30,
new Label("TOP (8 Carte):"), topRow,
new Label("CENTER (6 Carte):"), centerRow,
new Label("BOTTOM (7 Carte):"), bottomRow
);
tableArea.setAlignment(Pos.CENTER);
tableArea.setPadding(new Insets(20));
// Mettiamo il tavolo in uno ScrollPane nel caso le carte siano troppo grandi per lo schermo
ScrollPane scrollPane = new ScrollPane(tableArea);
scrollPane.setFitToWidth(true);
BorderPane root = new BorderPane();
root.setTop(topMenu);
root.setCenter(scrollPane);
// Finestra un po' più grande per contenere tutte queste carte
Scene scene = new Scene(root, 1200, 800);
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox createRowContainer() {
HBox row = new HBox(15); // 15px di spazio tra una carta e l'altra
row.setAlignment(Pos.CENTER);
return row;
}
private void loadPdf(Stage stage) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Seleziona il PDF del mazzo di carte");
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("File PDF", "*.pdf"));
File file = fileChooser.showOpenDialog(stage);
if (file != null) {
try {
if (document != null) {
document.close();
}
logger.info("Caricamento file PDF: {}", file.getAbsolutePath());
document = PDDocument.load(file);
pdfRenderer = new PDFRenderer(document);
totalCards = document.getNumberOfPages();
logger.info("PDF caricato. Pagine totali (carte): {}", totalCards);
btnShuffle.setDisable(false);
// Distribuisci le carte per la prima volta
distributeRandomCards();
} catch (IOException e) {
logger.error("Errore nel caricamento del PDF", e);
}
}
}
private void distributeRandomCards() {
if (document == null || totalCards == 0) return;
logger.info("Inizio rimescolamento e distribuzione carte...");
// Pulisce il tavolo dalle carte precedenti
topRow.getChildren().clear();
centerRow.getChildren().clear();
bottomRow.getChildren().clear();
// 1. Crea una lista con tutti gli indici (da 0 al numero totale di pagine)
List<Integer> deckIndices = new ArrayList<>();
for (int i = 0; i < totalCards; i++) {
deckIndices.add(i);
}
// 2. Mescola la lista (Simula la mescolata del mazzo)
Collections.shuffle(deckIndices);
// 3. Distribuisci nelle righe (evitando IndexOutOfBounds se il PDF ha meno di 21 pagine)
// Riga Top: 8 carte
populateRow(topRow, deckIndices, 0, 8);
// Riga Center: 6 carte
populateRow(centerRow, deckIndices, 8, 6);
// Riga Bottom: 7 carte
populateRow(bottomRow, deckIndices, 14, 7);
logger.info("Distribuzione completata.");
}
private void populateRow(HBox row, List<Integer> deckIndices, int startIndex, int numberOfCards) {
for (int i = 0; i < numberOfCards; i++) {
int actualIndex = startIndex + i;
// Se abbiamo esaurito le carte nel PDF, interrompiamo
if (actualIndex >= deckIndices.size()) {
logger.warn("Il PDF non ha abbastanza carte per riempire questa riga.");
break;
}
int pageNumber = deckIndices.get(actualIndex);
ImageView cardImage = createCardImageView(pageNumber);
if (cardImage != null) {
row.getChildren().add(cardImage);
}
}
}
private ImageView createCardImageView(int pageIndex) {
try {
// Renderizza la pagina. Usiamo 100 DPI invece di 150 per risparmiare RAM,
// dato che ora stiamo generando 21 immagini contemporaneamente.
BufferedImage bim = pdfRenderer.renderImageWithDPI(pageIndex, 100);
Image fxImage = SwingFXUtils.toFXImage(bim, null);
ImageView imageView = new ImageView(fxImage);
// Impostiamo l'altezza fissa per le carte, in modo che stiano sullo schermo
imageView.setFitHeight(180);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.5), 5, 0, 0, 0);");
return imageView;
} catch (IOException e) {
logger.error("Errore durante il rendering della pagina {}", pageIndex, e);
return null;
}
}
@Override
public void stop() throws Exception {
if (document != null) {
document.close();
logger.info("Documento PDF chiuso correttamente.");
}
logger.info("Applicazione terminata.");
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}

View File

@@ -0,0 +1,263 @@
package org.example.mesosll07;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
public class DeckGridApp2 extends Application {
private static final Logger logger = LogManager.getLogger(DeckGridApp2.class);
private PDDocument document;
private PDFRenderer pdfRenderer;
private int totalCards = 0;
// Contenitori Layout
private HBox topRow;
private HBox centerRow;
private HBox bottomRow;
private HBox playersArea; // NUOVO: Area per i giocatori
private Button btnShuffle;
// --- LOGICA DI GIOCO SIMULATA ---
public enum CardType {
CACCIA, UTENSILI, RITUALI, COSTRUZIONI
}
public static class Card {
int pdfPageIndex;
CardType type;
public Card(int pdfPageIndex, CardType type) {
this.pdfPageIndex = pdfPageIndex;
this.type = type;
}
public CardType getType() { return type; }
}
public static class Player {
String name;
int food;
int money;
List<Card> hand = new ArrayList<>();
public Player(String name, int food, int money) {
this.name = name;
this.food = food;
this.money = money;
}
public List<Card> getHand() { return hand; }
}
// --------------------------------
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Tavolo da Gioco - Board & Players");
Button btnLoad = new Button("Carica PDF Mazzo");
btnShuffle = new Button("Rimescola & Distribuisci");
btnShuffle.setDisable(true);
btnLoad.setOnAction(e -> loadPdf(primaryStage));
btnShuffle.setOnAction(e -> distributeAll());
HBox topMenu = new HBox(15, btnLoad, btnShuffle);
topMenu.setAlignment(Pos.CENTER);
topMenu.setPadding(new Insets(10));
topRow = createRowContainer();
centerRow = createRowContainer();
bottomRow = createRowContainer();
// NUOVO: Inizializza l'area giocatori
playersArea = new HBox(30);
playersArea.setAlignment(Pos.CENTER);
playersArea.setPadding(new Insets(20));
VBox tableArea = new VBox(20,
new Label("TOP (8 Carte):"), topRow,
new Label("CENTER (6 Carte):"), centerRow,
new Label("BOTTOM (7 Carte):"), bottomRow,
new Label("--- SITUAZIONE GIOCATORI ---"), playersArea
);
tableArea.setAlignment(Pos.CENTER);
tableArea.setPadding(new Insets(20));
ScrollPane scrollPane = new ScrollPane(tableArea);
scrollPane.setFitToWidth(true);
BorderPane root = new BorderPane();
root.setTop(topMenu);
root.setCenter(scrollPane);
Scene scene = new Scene(root, 1300, 900);
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox createRowContainer() {
HBox row = new HBox(10);
row.setAlignment(Pos.CENTER);
return row;
}
private void loadPdf(Stage stage) {
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("File PDF", "*.pdf"));
File file = fileChooser.showOpenDialog(stage);
if (file != null) {
try {
if (document != null) document.close();
document = PDDocument.load(file);
pdfRenderer = new PDFRenderer(document);
totalCards = document.getNumberOfPages();
btnShuffle.setDisable(false);
distributeAll();
} catch (IOException e) {
logger.error("Errore PDF", e);
}
}
}
private void distributeAll() {
if (document == null || totalCards == 0) return;
topRow.getChildren().clear();
centerRow.getChildren().clear();
bottomRow.getChildren().clear();
playersArea.getChildren().clear();
List<Integer> deck = new ArrayList<>();
for (int i = 0; i < totalCards; i++) deck.add(i);
Collections.shuffle(deck);
// Distribuisce sul tavolo le prime 21 carte
int index = 0;
index = populateTable(topRow, deck, index, 8);
index = populateTable(centerRow, deck, index, 6);
index = populateTable(bottomRow, deck, index, 7);
// NUOVO: Simula 2 giocatori con le carte rimanenti
Random rand = new Random();
List<Player> players = Arrays.asList(
new Player("Giocatore 1 (Tribù Rossa)", rand.nextInt(15), rand.nextInt(50)),
new Player("Giocatore 2 (Tribù Blu)", rand.nextInt(15), rand.nextInt(50))
);
// Assegna 5 carte a caso a ogni giocatore e simula il loro Tipo
CardType[] types = CardType.values();
for (Player p : players) {
for (int i = 0; i < 5; i++) {
if (index < deck.size()) {
CardType randomType = types[rand.nextInt(types.length)];
p.getHand().add(new Card(deck.get(index), randomType));
index++;
}
}
}
// Renderizza i giocatori nella UI
renderPlayers(players);
}
private int populateTable(HBox row, List<Integer> deck, int startIndex, int amount) {
for (int i = 0; i < amount; i++) {
if (startIndex >= deck.size()) break;
ImageView card = createCardImage(deck.get(startIndex), 150); // Altezza 150px
if (card != null) row.getChildren().add(card);
startIndex++;
}
return startIndex;
}
// --- NUOVO: RENDERIZZAZIONE GIOCATORI E RAGGRUPPAMENTO (JAVA 8) ---
private void renderPlayers(List<Player> players) {
for (Player player : players) {
VBox playerBox = new VBox(10);
playerBox.setPadding(new Insets(15));
playerBox.setStyle("-fx-border-color: #555; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-color: #f9f9f9; -fx-background-radius: 10;");
// Intestazione Giocatore
Label nameLbl = new Label(player.name);
nameLbl.setFont(Font.font("System", FontWeight.BOLD, 16));
// Risorse Cibo e Soldi
Label statsLbl = new Label("🍖 Cibo: " + player.food + " | 💰 Money: " + player.money);
statsLbl.setTextFill(Color.DARKRED);
statsLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
playerBox.getChildren().addAll(nameLbl, statsLbl);
// JAVA 8 MAGIA: Raggruppa le carte del giocatore per Tipo!
Map<CardType, List<Card>> groupedCards = player.getHand().stream()
.collect(Collectors.groupingBy(Card::getType));
// Itera sui gruppi creati e genera la grafica
groupedCards.forEach((type, cardsOfType) -> {
Label typeLbl = new Label("Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
HBox cardImagesRow = new HBox(5);
for (Card c : cardsOfType) {
// Carte più piccole (90px) per l'area giocatore
ImageView img = createCardImage(c.pdfPageIndex, 90);
if (img != null) cardImagesRow.getChildren().add(img);
}
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
});
playersArea.getChildren().add(playerBox);
}
}
private ImageView createCardImage(int pageIndex, int height) {
try {
BufferedImage bim = pdfRenderer.renderImageWithDPI(pageIndex, 100);
Image fxImage = SwingFXUtils.toFXImage(bim, null);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
return imageView;
} catch (IOException e) {
logger.error("Errore rendering pagina {}", pageIndex, e);
return null;
}
}
@Override
public void stop() throws Exception {
if (document != null) document.close();
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}

View File

@@ -0,0 +1,271 @@
package org.example.mesosll07;
import Server.Automaton.Game;
import Server.Era;
import Server.Player;
import Server.TotemColor;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import Server.Cards.*;
import java.nio.file.Paths;
public class DeckGridAppFX extends Application {
private static final Logger logger = LogManager.getLogger(DeckGridAppFX.class);
private PDDocument documentFront;
private PDDocument documentBack;
private PDFRenderer pdfRendererFront;
private PDFRenderer pdfRendererBack;
private int totalCards = 0;
// Contenitori Layout
private HBox topRow;
private HBox centerRow;
private HBox bottomRow;
private HBox playersArea; // NUOVO: Area per i giocatori
private Button btnShuffle;
// --------------------------------
@Override
public void start(Stage primaryStage) {
List<Player> players = new ArrayList<>();
players.add(new Player("Yoshi", TotemColor.YELLOW));
players.add(new Player("Pippo", TotemColor.PURPLE));
players.add(new Player("John", TotemColor.RED));
players.add(new Player("Baggio", TotemColor.BLUE));
players.add(new Player("Gino", TotemColor.GREEN));
Game game = new Game(players);
String fileCards="/home/lorenzo/dev/Mesos2/src/main/resources/files/cards.csv";
String fileCardsImgFront="/home/lorenzo/dev/Mesos2/src/main/resources/files/Cards_total_front_PROMO.pdf";
String fileCardsImgCover="/home/lorenzo/dev/Mesos2/src/main/resources/files/Cards_total_back_PROMO.pdf";
try {
File fileFront = new File(fileCardsImgFront);
File fileBack = new File(fileCardsImgCover);
documentFront = PDDocument.load(fileFront);
pdfRendererFront = new PDFRenderer(documentFront);
documentBack = PDDocument.load(fileBack);
pdfRendererBack = new PDFRenderer(documentBack);
} catch ( IOException e) {
throw new RuntimeException(e);
}
game.newGame(fileCards);
primaryStage.setTitle("Tavolo da Gioco - Board & Players");
btnShuffle = new Button("Rimescola & Distribuisci");
btnShuffle.setDisable(true);
btnShuffle.setOnAction(e -> distributeAll(game));
HBox topMenu = new HBox(15, btnShuffle);
topMenu.setAlignment(Pos.CENTER);
topMenu.setPadding(new Insets(10));
topRow = createRowContainer();
centerRow = createRowContainer();
bottomRow = createRowContainer();
// NUOVO: Inizializza l'area giocatori
playersArea = new HBox(30);
playersArea.setAlignment(Pos.CENTER);
playersArea.setPadding(new Insets(20));
distributeAll(game);
VBox tableArea = new VBox(20,
new Label("TOP"), topRow,
new Label("CENTER "), centerRow,
new Label("DOWN"), bottomRow,
new Label("--- SITUAZIONE GIOCATORI ---"), playersArea
);
tableArea.setAlignment(Pos.CENTER);
tableArea.setPadding(new Insets(20));
ScrollPane scrollPane = new ScrollPane(tableArea);
scrollPane.setFitToWidth(true);
BorderPane root = new BorderPane();
root.setTop(topMenu);
root.setCenter(scrollPane);
Scene scene = new Scene(root, 1300, 900);
primaryStage.setScene(scene);
primaryStage.show();
}
private HBox createRowContainer() {
HBox row = new HBox(10);
row.setAlignment(Pos.CENTER);
return row;
}
private void distributeAll(Game game) {
if (documentFront == null) return;
topRow.getChildren().clear();
centerRow.getChildren().clear();
bottomRow.getChildren().clear();
playersArea.getChildren().clear();
List<Integer> deck = new ArrayList<>();
for (int i = 0; i < 118; i++) deck.add(i);
Collections.shuffle(deck);
// Distribuisce sul tavolo le prime 21 carte
int index = 0;
index = populateTable(topRow, deck, index, 8);
index = populateTable(centerRow, deck, index, 6);
index = populateTable(bottomRow, deck, index, 7);
// NUOVO: Simula 2 giocatori con le carte rimanenti
Random rand = new Random();
List<Player> players =game.getPlayers();
// Assegna 5 carte a caso a ogni giocatore e simula il loro Tipo
for (Player p : players) {
for (int j=0;j<4;j++){
int idx = rand.nextInt(game.getGameBoard().getCardDeck().getTribeDeck().size());
Card c = game.getGameBoard().getCardDeck().drawTribeOne();
p.getPlayerTribe().addCharacter((CharacterCard) c);
}
for (int j=0;j<2;j++){
int idx = rand.nextInt(game.getGameBoard().getCardDeck().getBuildingDeck(Era.I).size());
Card c = game.getGameBoard().getCardDeck().drawBuildingOne(Era.I);
p.getPlayerTribe().addBuilding((BuildingCard) c);
}
}
// Renderizza i giocatori nella UI
renderPlayers(players);
}
private int populateTable(HBox row, List<Integer> deck, int startIndex, int amount) {
for (int i = 0; i < amount; i++) {
if (startIndex >= deck.size()) break;
ImageView card = createCardImage(deck.get(startIndex), 150, true); // Altezza 150px
if (card != null) row.getChildren().add(card);
startIndex++;
}
return startIndex;
}
// --- NUOVO: RENDERIZZAZIONE GIOCATORI E RAGGRUPPAMENTO (JAVA 8) ---
private void renderPlayers(List<Player> players) {
for (Player player : players) {
VBox playerBox = new VBox(10);
playerBox.setPadding(new Insets(15));
playerBox.setStyle("-fx-border-color: #555; -fx-border-width: 2; -fx-border-radius: 10; -fx-background-color: #f9f9f9; -fx-background-radius: 10;");
// Intestazione Giocatore
Label nameLbl = new Label(player.getNickname());
nameLbl.setFont(Font.font("System", FontWeight.BOLD, 16));
// Risorse Cibo e Soldi
Label statsLbl = new Label("🍖 Cibo: " + player.getFoodTokens() + " | 💰 Money: " + player.getPrestigePoints());
statsLbl.setTextFill(Color.DARKRED);
statsLbl.setFont(Font.font("System", FontWeight.BOLD, 14));
playerBox.getChildren().addAll(nameLbl, statsLbl);
// JAVA 8 MAGIA: Raggruppa le carte del giocatore per Tipo!
Map<CharacterType, List<CharacterCard>> groupedCards = player.getPlayerTribe().getCharacters().stream()
.collect(Collectors.groupingBy(CharacterCard::getCharacterType));
// Itera sui gruppi creati e genera la grafica
groupedCards.forEach((type, cardsOfType) -> {
Label typeLbl = new Label("Tipo: " + type.name() + " (" + cardsOfType.size() + ")");
typeLbl.setFont(Font.font("System", FontWeight.NORMAL, 12));
HBox cardImagesRow = new HBox(5);
for (Card c : cardsOfType) {
// Carte più piccole (90px) per l'area giocatore
ImageView img = createCardImage(c.getCardId(), 90, true);
if (img != null) cardImagesRow.getChildren().add(img);
}
playerBox.getChildren().addAll(typeLbl, cardImagesRow);
});
playersArea.getChildren().add(playerBox);
}
}
private ImageView createCardImage(int pageIndex, int height, boolean front) {
try {
BufferedImage bim =null;
if (front) bim = pdfRendererFront.renderImageWithDPI(pageIndex, 100);
else bim = pdfRendererBack.renderImageWithDPI(pageIndex, 100);
Image fxImage = SwingFXUtils.toFXImage(bim, null);
ImageView imageView = new ImageView(fxImage);
imageView.setFitHeight(height);
imageView.setPreserveRatio(true);
imageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 4, 0, 0, 0);");
return imageView;
} catch (IOException e) {
logger.error("Errore rendering pagina {}", pageIndex, e);
return null;
}
}
@Override
public void stop() throws Exception {
if (documentFront != null) documentFront.close();
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}

View File

@@ -0,0 +1,159 @@
package org.example.mesosll07;
import Server.Automaton.Game;
import Server.Player;
import Server.TotemColor;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class DeckViewerFX extends Application {
private PDDocument document;
private PDFRenderer pdfRenderer;
private int currentIndex = 0;
private int totalCards = 0;
private ImageView cardImageView;
private Label pageLabel;
private Button btnPrev;
private Button btnNext;
@Override
public void start(Stage primaryStage) {
List<Player> players = new ArrayList<>();
players.add(new Player("Yoshi", TotemColor.YELLOW));
players.add(new Player("Pippo", TotemColor.PURPLE));
players.add(new Player("John", TotemColor.RED));
players.add(new Player("Baggio", TotemColor.BLUE));
players.add(new Player("Gino", TotemColor.GREEN));
Game game = new Game(players);
game.newGame("/home/lorenzo/dev/Mesos2/src/main/resources/files/cards.csv");
primaryStage.setTitle("Visualizzatore Mazzo di Carte (PDF)");
// Componente per mostrare la carta
cardImageView = new ImageView();
cardImageView.setFitHeight(500); // Altezza fissa, larghezza in proporzione
cardImageView.setPreserveRatio(true);
cardImageView.setStyle("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.5), 10, 0, 0, 0);");
// Controlli UI
btnPrev = new Button("⬅ Carta Precedente");
btnNext = new Button("Carta Successiva ➡");
pageLabel = new Label("Nessun mazzo caricato");
Button btnLoad = new Button("Carica PDF Mazzo");
// Azioni dei pulsanti
btnLoad.setOnAction(e -> loadPdf(primaryStage));
btnPrev.setOnAction(e -> showCard(currentIndex - 1));
btnNext.setOnAction(e -> showCard(currentIndex + 1));
updateButtons();
// Layout
HBox controls = new HBox(15, btnPrev, pageLabel, btnNext);
controls.setAlignment(Pos.CENTER);
VBox topBox = new VBox(10, btnLoad);
topBox.setAlignment(Pos.CENTER);
topBox.setPadding(new Insets(10));
BorderPane root = new BorderPane();
root.setTop(topBox);
root.setCenter(cardImageView);
root.setBottom(controls);
BorderPane.setMargin(cardImageView, new Insets(20));
BorderPane.setMargin(controls, new Insets(20));
Scene scene = new Scene(root, 600, 700);
primaryStage.setScene(scene);
primaryStage.show();
}
private void loadPdf(Stage stage) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Seleziona il PDF del mazzo di carte");
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("File PDF", "*.pdf"));
File file = fileChooser.showOpenDialog(stage);
if (file != null) {
try {
// Chiude il documento precedente se esiste
if (document != null) {
document.close();
}
// Carica il nuovo PDF
document = PDDocument.load(file);
pdfRenderer = new PDFRenderer(document);
totalCards = document.getNumberOfPages();
// Mostra la prima carta
showCard(0);
} catch (IOException e) {
e.printStackTrace();
pageLabel.setText("Errore nel caricamento del file!");
}
}
}
private void showCard(int index) {
if (document == null || index < 0 || index >= totalCards) return;
try {
// Renderizza la pagina del PDF in un'immagine BufferedImage (DPI 150 per buona qualità)
BufferedImage bim = pdfRenderer.renderImageWithDPI(index, 150);
// Converte l'immagine di AWT (Swing) in un'immagine JavaFX
Image fxImage = SwingFXUtils.toFXImage(bim, null);
cardImageView.setImage(fxImage);
currentIndex = index;
pageLabel.setText("Carta " + (currentIndex + 1) + " di " + totalCards);
updateButtons();
} catch (IOException e) {
e.printStackTrace();
}
}
private void updateButtons() {
btnPrev.setDisable(document == null || currentIndex == 0);
btnNext.setDisable(document == null || currentIndex == totalCards - 1);
}
@Override
public void stop() throws Exception {
// Importante: chiudere il documento per liberare la memoria quando l'app si chiude
if (document != null) {
document.close();
}
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}

View File

@@ -0,0 +1,19 @@
package org.example.mesosll07;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
}

View File

@@ -0,0 +1,14 @@
package org.example.mesosll07;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class HelloController {
@FXML
private Label welcomeText;
@FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
}
}

View File

@@ -0,0 +1,9 @@
package org.example.mesosll07;
import javafx.application.Application;
public class Launcher {
public static void main(String[] args) {
Application.launch(HelloApplication.class, args);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1,117 @@
C;1;0;I;HUNTER;1;0
C;2;0;I;HUNTER;1;0
C;3;0;I;HUNTER;0;0
C;4;3;I;HUNTER;0;0
C;5;3;I;HUNTER;0;0
C;6;0;I;BUILDER;1;3
C;7;0;I;BUILDER;2;0
C;8;5;I;BUILDER;2;1
C;9;0;I;BUILDER;1;2
C;10;0;I;GATHERER;3;0
C;11;0;I;GATHERER;3;0
C;12;3;I;GATHERER;3;0
C;13;5;I;GATHERER;3;0
C;14;0;I;ARTIST;0;0
C;15;0;I;ARTIST;0;0
C;16;0;I;ARTIST;0;0
C;17;3;I;ARTIST;0;0
C;18;4;I;ARTIST;0;0
C;19;0;I;INVENTOR;8;0
C;20;0;I;INVENTOR;0;0
C;21;0;I;INVENTOR;1;0
C;22;0;I;INVENTOR;9;0
C;23;4;I;INVENTOR;5;0
C;24;4;I;INVENTOR;7;0
C;25;4;I;INVENTOR;4;0
C;26;5;I;SHAMAN;2;0
C;27;0;I;SHAMAN;2;0
C;28;0;I;SHAMAN;1;0
C;29;4;I;SHAMAN;1;0
C;30;0;II;HUNTER;0;0
C;31;0;II;HUNTER;0;0
C;32;3;II;HUNTER;1;0
C;33;0;II;HUNTER;1;0
C;34;4;II;HUNTER;1;0
C;35;5;II;HUNTER;0;0
C;36;0;II;BUILDER;1;4
C;37;0;II;BUILDER;2;1
C;38;3;II;BUILDER;1;2
C;39;0;II;BUILDER;2;3
C;40;0;II;GATHERER;3;0
C;41;3;II;GATHERER;3;0
C;42;4;II;GATHERER;3;0
C;43;5;II;GATHERER;3;0
C;44;3;II;ARTIST;0;0
C;45;0;II;ARTIST;0;0
C;46;0;II;ARTIST;0;0
C;47;0;II;ARTIST;0;0
C;48;0;II;INVENTOR;0;0
C;49;4;II;INVENTOR;0;0
C;50;0;II;INVENTOR;0;0
C;51;0;II;INVENTOR;0;0
C;52;0;II;INVENTOR;0;0
C;53;0;II;INVENTOR;0;0
C;54;0;II;SHAMAN;2;0
C;55;0;II;SHAMAN;2;0
C;56;5;II;SHAMAN;1;0
C;57;5;II;SHAMAN;2;0
C;58;5;III;HUNTER;1;0
C;59;0;III;HUNTER;0;0
C;60;0;III;HUNTER;0;0
C;61;0;III;HUNTER;1;0
C;62;0;III;BUILDER;1;5
C;63;0;III;BUILDER;2;3
C;64;5;III;BUILDER;1;4
C;65;0;III;BUILDER;2;2
C;66;5;III;GATHERER;3;0
C;67;4;III;GATHERER;3;0
C;68;0;III;GATHERER;3;0
C;69;5;III;ARTIST;0;0
C;70;0;III;ARTIST;0;0
C;71;0;III;ARTIST;0;0
C;72;0;III;ARTIST;0;0
C;73;4;III;INVENTOR;0;0
C;74;3;III;INVENTOR;0;0
C;75;3;III;INVENTOR;0;0
C;76;0;III;INVENTOR;0;0
C;77;0;III;INVENTOR;0;0
C;78;0;III;INVENTOR;0;0
C;79;0;III;INVENTOR;0;0
C;80;3;III;SHAMAN;2;0
C;81;0;III;SHAMAN;3;0
C;82;0;III;SHAMAN;2;0
C;83;0;III;SHAMAN;3;0
C;84;4;III;SHAMAN;2;0
E;85;0;I;HUNT;1;1
E;86;0;I;SUSTAINMENT;1;1
E;87;0;I;SHAMANIC_RITUAL;5;3
E;88;0;I;CAVE_PAINTINGS;2;1
E;89;0;II;HUNT;1;2
E;90;0;II;SUSTAINMENT;1;2
E;91;0;II;SHAMANIC_RITUAL;10;5
E;92;0;II;CAVE_PAINTINGS;2;2
E;93;0;III;HUNT;1;3
E;94;0;III;CAVE_PAINTINGS;2;3
E;95;0;FINAL;SUSTAINMENT;1;3
E;96;0;FINAL;SHAMANIC_RITUAL;15;7
B;97;0;I;4;3;FOOD_FOR_SIX
B;98;0;I;4;4;SUSTAIN_DISCOUNT
B;99;0;I;5;3;SUSTAIN_DISCOUNT
B;100;0;I;5;2;SHAMAN_NO_LOSS
B;101;0;I;3;3;BONUS_FOOD_ENDTURN
B;102;0;I;3;4;FOOD_PER_INVENTORS
B;103;0;II;7;0;SHAMAN_DOUBLE_POINTS
B;104;0;II;6;4;SHAMAN_BONUS
B;105;0;II;7;4;SUSTAIN_DISCOUNT
B;106;0;II;7;2;HUNT_BONUS
B;107;0;II;6;4;ENDGAME_BUILDER_BONUS
B;108;0;II;5;6;PAINTING_FOOD_BONUS
B;109;0;II;5;6;ENDGAME_FOR_SIX
B;110;0;III;8;8;ENDGAME_BONUS_CHARACTER
B;111;0;III;7;6;ENDGAME_BONUS_CHARACTER
B;112;0;III;7;4;ENDGAME_BONUS_CHARACTER
B;113;0;III;6;3;ENDGAME_BONUS_CHARACTER
B;114;0;III;7;4;ENDGAME_BONUS_CHARACTER
B;115;0;III;6;6;ENDGAME_BONUS_CHARACTER
B;116;0;III;9;3;EXTRA_DRAW
B;117;0;III;10;0;ENDGAME_BONUS_POINTS
1 C 1 0 I HUNTER 1 0
2 C 2 0 I HUNTER 1 0
3 C 3 0 I HUNTER 0 0
4 C 4 3 I HUNTER 0 0
5 C 5 3 I HUNTER 0 0
6 C 6 0 I BUILDER 1 3
7 C 7 0 I BUILDER 2 0
8 C 8 5 I BUILDER 2 1
9 C 9 0 I BUILDER 1 2
10 C 10 0 I GATHERER 3 0
11 C 11 0 I GATHERER 3 0
12 C 12 3 I GATHERER 3 0
13 C 13 5 I GATHERER 3 0
14 C 14 0 I ARTIST 0 0
15 C 15 0 I ARTIST 0 0
16 C 16 0 I ARTIST 0 0
17 C 17 3 I ARTIST 0 0
18 C 18 4 I ARTIST 0 0
19 C 19 0 I INVENTOR 8 0
20 C 20 0 I INVENTOR 0 0
21 C 21 0 I INVENTOR 1 0
22 C 22 0 I INVENTOR 9 0
23 C 23 4 I INVENTOR 5 0
24 C 24 4 I INVENTOR 7 0
25 C 25 4 I INVENTOR 4 0
26 C 26 5 I SHAMAN 2 0
27 C 27 0 I SHAMAN 2 0
28 C 28 0 I SHAMAN 1 0
29 C 29 4 I SHAMAN 1 0
30 C 30 0 II HUNTER 0 0
31 C 31 0 II HUNTER 0 0
32 C 32 3 II HUNTER 1 0
33 C 33 0 II HUNTER 1 0
34 C 34 4 II HUNTER 1 0
35 C 35 5 II HUNTER 0 0
36 C 36 0 II BUILDER 1 4
37 C 37 0 II BUILDER 2 1
38 C 38 3 II BUILDER 1 2
39 C 39 0 II BUILDER 2 3
40 C 40 0 II GATHERER 3 0
41 C 41 3 II GATHERER 3 0
42 C 42 4 II GATHERER 3 0
43 C 43 5 II GATHERER 3 0
44 C 44 3 II ARTIST 0 0
45 C 45 0 II ARTIST 0 0
46 C 46 0 II ARTIST 0 0
47 C 47 0 II ARTIST 0 0
48 C 48 0 II INVENTOR 0 0
49 C 49 4 II INVENTOR 0 0
50 C 50 0 II INVENTOR 0 0
51 C 51 0 II INVENTOR 0 0
52 C 52 0 II INVENTOR 0 0
53 C 53 0 II INVENTOR 0 0
54 C 54 0 II SHAMAN 2 0
55 C 55 0 II SHAMAN 2 0
56 C 56 5 II SHAMAN 1 0
57 C 57 5 II SHAMAN 2 0
58 C 58 5 III HUNTER 1 0
59 C 59 0 III HUNTER 0 0
60 C 60 0 III HUNTER 0 0
61 C 61 0 III HUNTER 1 0
62 C 62 0 III BUILDER 1 5
63 C 63 0 III BUILDER 2 3
64 C 64 5 III BUILDER 1 4
65 C 65 0 III BUILDER 2 2
66 C 66 5 III GATHERER 3 0
67 C 67 4 III GATHERER 3 0
68 C 68 0 III GATHERER 3 0
69 C 69 5 III ARTIST 0 0
70 C 70 0 III ARTIST 0 0
71 C 71 0 III ARTIST 0 0
72 C 72 0 III ARTIST 0 0
73 C 73 4 III INVENTOR 0 0
74 C 74 3 III INVENTOR 0 0
75 C 75 3 III INVENTOR 0 0
76 C 76 0 III INVENTOR 0 0
77 C 77 0 III INVENTOR 0 0
78 C 78 0 III INVENTOR 0 0
79 C 79 0 III INVENTOR 0 0
80 C 80 3 III SHAMAN 2 0
81 C 81 0 III SHAMAN 3 0
82 C 82 0 III SHAMAN 2 0
83 C 83 0 III SHAMAN 3 0
84 C 84 4 III SHAMAN 2 0
85 E 85 0 I HUNT 1 1
86 E 86 0 I SUSTAINMENT 1 1
87 E 87 0 I SHAMANIC_RITUAL 5 3
88 E 88 0 I CAVE_PAINTINGS 2 1
89 E 89 0 II HUNT 1 2
90 E 90 0 II SUSTAINMENT 1 2
91 E 91 0 II SHAMANIC_RITUAL 10 5
92 E 92 0 II CAVE_PAINTINGS 2 2
93 E 93 0 III HUNT 1 3
94 E 94 0 III CAVE_PAINTINGS 2 3
95 E 95 0 FINAL SUSTAINMENT 1 3
96 E 96 0 FINAL SHAMANIC_RITUAL 15 7
97 B 97 0 I 4 3 FOOD_FOR_SIX
98 B 98 0 I 4 4 SUSTAIN_DISCOUNT
99 B 99 0 I 5 3 SUSTAIN_DISCOUNT
100 B 100 0 I 5 2 SHAMAN_NO_LOSS
101 B 101 0 I 3 3 BONUS_FOOD_ENDTURN
102 B 102 0 I 3 4 FOOD_PER_INVENTORS
103 B 103 0 II 7 0 SHAMAN_DOUBLE_POINTS
104 B 104 0 II 6 4 SHAMAN_BONUS
105 B 105 0 II 7 4 SUSTAIN_DISCOUNT
106 B 106 0 II 7 2 HUNT_BONUS
107 B 107 0 II 6 4 ENDGAME_BUILDER_BONUS
108 B 108 0 II 5 6 PAINTING_FOOD_BONUS
109 B 109 0 II 5 6 ENDGAME_FOR_SIX
110 B 110 0 III 8 8 ENDGAME_BONUS_CHARACTER
111 B 111 0 III 7 6 ENDGAME_BONUS_CHARACTER
112 B 112 0 III 7 4 ENDGAME_BONUS_CHARACTER
113 B 113 0 III 6 3 ENDGAME_BONUS_CHARACTER
114 B 114 0 III 7 4 ENDGAME_BONUS_CHARACTER
115 B 115 0 III 6 6 ENDGAME_BONUS_CHARACTER
116 B 116 0 III 9 3 EXTRA_DRAW
117 B 117 0 III 10 0 ENDGAME_BONUS_POINTS

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!-- Appender per scrivere sulla Console -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- Appender per scrivere su un file chiamato app.log -->
<File name="LogFile" fileName="logs/mesos2.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
</File>
</Appenders>
<Loggers>
<!-- Configurazione base: registra tutto ciò che è livello INFO o superiore -->
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="LogFile"/>
</Root>
</Loggers>
</Configuration>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
fx:controller="org.example.mesosll07.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label fx:id="welcomeText"/>
<Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>

View File

@@ -0,0 +1,36 @@
package Server.Automaton;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class GameTest {
@Test
void newGame() {
}
@Test
void placeTotem() {
}
@Test
void resolveCardAction() {
}
@Test
void extraDrawAction() {
}
@Test
void skipExtraDraw() {
}
@Test
void endGame() {
}
@Test
void getPendingActions() {
}
}

View File

@@ -0,0 +1,205 @@
package Server;
import Server.Cards.BuildingCard;
import Server.Cards.CardDeck;
import Server.Cards.CharacterCard;
import Server.Cards.EventCard;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.ArrayList;
import java.util.List;
class BuildingManagerTest {
static List<Player> players = new ArrayList<>();
@BeforeAll
static void setUp(){
BuildingManager buildingManager = new BuildingManager();
Player winner = new Player("Winner", TotemColor.YELLOW);
winner.addFood(10);
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;1;2;I;SHAMAN;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;2;2;I;SHAMAN;2;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;3;2;I;BUILDER;3;1"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;14;0;I;ARTIST;0;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;15;0;I;ARTIST;0;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;21;0;I;ARTIST;1;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;22;0;I;INVENTOR;9;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;23;4;I;INVENTOR;5;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;43;0;I;HUNTER;0;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;44;3;I;HUNTER;0;0"));
players.add(winner);
Player medium = new Player("Medium", TotemColor.RED);
medium.addFood(10);
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;56;2;I;SHAMAN;3;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;34;2;I;BUILDER;3;1"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;32;3;I;GATHERER;3;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;33;0;I;ARTIST;0;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;45;3;I;HUNTER;0;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;72;0;I;ARTIST;9;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;73;4;I;INVENTOR;5;0"));
players.add(medium);
Player loser = new Player("Loser", TotemColor.BLUE);
loser.addFood(2);
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;54;2;I;SHAMAN;3;0"));
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;36;2;I;BUILDER;3;1"));
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;82;0;I;INVENTOR;9;0"));
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;93;4;I;INVENTOR;5;0"));
players.add(loser);
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/only_building_deck.csv", 5);
//for(BuildingCard c: cardDeck.drawBuilding(30)){
// buildingManager.addActiveBuilding(c, players.getFirst());
//}
//buildingManager.addActiveBuilding(BuildingCard.parsRow("B;5;3;III;6;2;SHAMAN_NO_LOSS"), players.get(2));
}
@Test
void foodForSix() {
int initialFood = players.getFirst().getFoodTokens();
CharacterCard cardForSix = CharacterCard.parsRow("C;39;2;I;BUILDER;3;1");
BuildingManager.foodForSix(players.getFirst(), cardForSix);
assertEquals(initialFood + 6, players.getFirst().getFoodTokens());
players.getFirst().getPlayerTribe().addCharacter(cardForSix);
BuildingManager.foodForSix(players.getFirst(), cardForSix);
assertEquals(initialFood + 6, players.getFirst().getFoodTokens());
}
@Test
void sustainDiscount() {
EventCard sustainment = EventCard.parsRow("E;1;0;I;SUSTAINMENT;2;0");
ArrayList<Integer> target = new ArrayList<>();
target.add(players.getFirst().getFoodTokens());
target.add(players.getFirst().getPrestigePoints());
target.add(players.get(1).getFoodTokens() - 4);
target.add(players.get(1).getPrestigePoints());
target.add(0);
target.add(players.get(2).getPrestigePoints()-4);
assertEquals(target, EventsSolver.sustainment(sustainment, players));
}
@Test
void shamanicBuildings() {
EventCard shamanicRitual = EventCard.parsRow("E;1;0;I;SHAMANIC_RITUAL;10;5");
ArrayList<Integer> target = new ArrayList<>();
target.add(players.getFirst().getPrestigePoints() + 20);
target.add(players.get(1).getPrestigePoints()-5);
target.add(players.get(2).getPrestigePoints());
assertEquals(target, EventsSolver.shamanicRitual(shamanicRitual, players));
}
@Test
void bonusEndTurn() {
assertEquals(1, BuildingManager.bonusEndTurn(players.getFirst(), 1));
assertEquals(0, BuildingManager.bonusEndTurn(players.getFirst(), 0));
assertEquals(0, BuildingManager.bonusEndTurn(players.getFirst(), -1));
}
@Test
void foodPerInventors() {
int initialFood = players.getFirst().getFoodTokens();
CharacterCard builder = CharacterCard.parsRow("C;57;2;I;INVENTOR;9;0");
BuildingManager.foodPerInventors(players.getFirst(), builder);
assertEquals(initialFood + 3, players.getFirst().getFoodTokens());
players.getFirst().getPlayerTribe().addCharacter(builder);
BuildingManager.foodPerInventors(players.getFirst(), builder);
assertEquals(initialFood+3, players.getFirst().getFoodTokens());
initialFood = players.get(2).getFoodTokens();
BuildingManager.foodPerInventors(players.get(2), builder);
assertEquals(initialFood, players.get(2).getFoodTokens());
}
@Test
void shamanBonus() {
assertEquals(3, BuildingManager.shamanBonus(players.getFirst()));
assertEquals(0, BuildingManager.shamanBonus(players.get(1)));
}
@Test
void hunterBonus() {
EventCard hunt = EventCard.parsRow("E;1;0;I;HUNT;2;0");
ArrayList<Integer> target = new ArrayList<>();
target.add(players.getFirst().getFoodTokens()+4);
target.add(players.getFirst().getPrestigePoints()+6);
target.add(players.get(1).getFoodTokens()+1);
target.add(players.get(1).getPrestigePoints()+2);
target.add(players.get(2).getFoodTokens());
target.add(players.get(2).getPrestigePoints());
assertEquals(target, EventsSolver.hunt(hunt, players));
}
@Test
void endgameBuilderBonus() {
}
@Test
void paintingFoodBonus() {
int winnerInitial = players.getFirst().getFoodTokens();
int middleInitial = players.get(1).getFoodTokens();
EventCard cavePainting = EventCard.parsRow("E;4;4;FINAL;CAVE_PAINTINGS;2;2");
EventsSolver.cavePaintings(cavePainting, players);
assertEquals(winnerInitial + 3 , players.getFirst().getFoodTokens());
assertEquals(middleInitial, players.get(1).getFoodTokens());
}
@Test
void endgameForSix() {
int winnerInitial = players.getFirst().getPrestigePoints();
int middleInitial = players.get(1).getPrestigePoints();
BuildingManager.endgameForSix(players.getFirst());
BuildingManager.endgameForSix(players.get(1));
assertEquals(winnerInitial + 6 , players.getFirst().getPrestigePoints());
assertEquals(middleInitial, players.get(1).getPrestigePoints());
}
@Test
void endgameBonusCharacter() {
int winnerInitial = players.getFirst().getPrestigePoints();
int middleInitial = players.get(1).getPrestigePoints();
BuildingManager.endgameBonusCharacter(players.getFirst());
BuildingManager.endgameBonusCharacter(players.get(1));
assertEquals(winnerInitial + 9 , players.getFirst().getPrestigePoints());
assertEquals(middleInitial, players.get(1).getPrestigePoints());
}
@Test
void extraDraw() {
assertTrue(BuildingManager.extraDraw(players.getFirst()));
assertFalse(BuildingManager.extraDraw(players.get(1)));
}
@Test
void endgameBonusPoints() {
int initialWinner = players.getFirst().getPrestigePoints();
int initialMedium = players.get(1).getPrestigePoints();
BuildingManager.endgameBonusPoints(players.getFirst());
BuildingManager.endgameBonusPoints(players.get(1));
assertEquals(initialWinner+25, players.getFirst().getPrestigePoints());
assertEquals(initialMedium, players.get(1).getPrestigePoints());
}
}

View File

@@ -0,0 +1,39 @@
package Server.Cards;
import org.junit.jupiter.api.Test;
import static Server.Era.I;
import static Server.Era.II;
import static org.junit.jupiter.api.Assertions.*;
class BuildingCardTest {
//Test if the method parsRow is able to create a correct BuildingCard
@Test
void registerBuildingCard() {
BuildingCard buildingCard = BuildingCard.parsRow("B;1;2;I;3;1;SHAMAN_BONUS");
assertEquals(1, buildingCard.getCardId());
assertEquals(2, buildingCard.getForMinPlayer());
assertEquals(I, buildingCard.getEra());
assertEquals(3, buildingCard.getCost());
assertEquals(1, buildingCard.getCardId());
assertEquals(Trigger.SHAMAN_BONUS, buildingCard.getAbilityTrigger());
assertNull(buildingCard.getEffectTarget());
}
void registerBuildingCardWithTarget() {
BuildingCard buildingCard = BuildingCard.parsRow("B;17;5;II;6;2;SUSTAIN_DISCOUNT;ARTIST");
assertEquals(17, buildingCard.getCardId());
assertEquals(5, buildingCard.getForMinPlayer());
assertEquals(II, buildingCard.getEra());
assertEquals(6, buildingCard.getCost());
assertEquals(2, buildingCard.getCardId());
assertEquals(Trigger.SUSTAIN_DISCOUNT, buildingCard.getAbilityTrigger());
assertEquals(CharacterType.ARTIST, buildingCard.getEffectTarget());
}
}

View File

@@ -0,0 +1,103 @@
package Server.Cards;
import org.junit.jupiter.api.Test;
import java.util.List;
import Server.Era;
import static org.junit.jupiter.api.Assertions.*;
class CardDeckTest {
//Load 2 cards from CSV to cardDeck
@Test
void loadOnlyCharacter(){
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/only_character_deck.csv", 4);
assertEquals(2, cardDeck.getTribeDeck().size());
}
//First create a new CardDeck filled with a 14 cards then he recreates said deck
// with a different set of cards to emulate a new game
@Test
void doubleLoad(){
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/only_building_deck.csv", 4);
assertEquals(0, cardDeck.getTribeDeck().size());
assertEquals(6, cardDeck.getBuildingDeck(Era.I).size());
assertEquals(7, cardDeck.getBuildingDeck(Era.II).size());
assertEquals(8, cardDeck.getBuildingDeck(Era.III).size());
cardDeck.setForNPlayer("./src/test/resources/files/only_event_deck.csv", 4);
assertEquals(3, cardDeck.getTribeDeck().size());
assertEquals(0, cardDeck.getBuildingDeck(Era.I).size());
assertEquals(0, cardDeck.getBuildingDeck(Era.II).size());
assertEquals(0, cardDeck.getBuildingDeck(Era.III).size());
}
//Draw 5 cards from TribeDeck and 3 from BuildingDeck, then check if they are correctly
// removed from cardDeck
@Test
void draw() {
int tribeDraw = 5;
int buildingDraw = 3;
int drawnCards = tribeDraw + buildingDraw;
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/test_deck.csv", 5);
//int deckSize = cardDeck.getTribeDeck().size() + cardDeck.getBuildingDeck().size();
List<Card> cards = cardDeck.drawTribe(tribeDraw);
assertEquals(tribeDraw, cards.size());
//List<BuildingCard> buildingCards = cardDeck.drawBuilding(buildingDraw);
//assertEquals(buildingDraw, buildingCards.size());
//int newDeckSize = cardDeck.getTribeDeck().size() + cardDeck.getBuildingDeck().size();
//assertEquals(deckSize - drawnCards, newDeckSize);
}
//draw 5 cards from TribeDeck
@Test
void drawTribe() {
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/test_deck.csv", 5);
List<Card> cards = cardDeck.drawTribe(5);
assertEquals(5, cards.size());
}
//draw 3 cards from BuildingDeck
@Test
void drawBuilding() {
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/test_deck.csv", 5);
// List<BuildingCard> buildingCards = cardDeck.drawBuilding(3);
// assertEquals(3, buildingCards.size());
}
//Try to draw 5 cards from a deck of only 3, then he returns only the remaining
//cards in the deck
@Test
void drawTooManyCards() {
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/only_character_deck.csv", 5);
List<Card> cards = cardDeck.drawTribe(5);
assertEquals(3, cards.size());
}
@Test
void shuffledDeck() {
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/test_deck.csv", 5);
assertEquals(Era.I, cardDeck.drawTribe(1).getFirst().getEra());
assertEquals(Era.FINAL, cardDeck.drawTribe(100).getLast().getEra());
// assertEquals(Era.I, cardDeck.drawBuilding(1).getFirst().getEra());
// assertEquals(Era.III, cardDeck.drawBuilding(100).getLast().getEra());
}
}

View File

@@ -0,0 +1,36 @@
package Server.Cards;
import Server.Utils.LoadingCardsException;
import org.junit.jupiter.api.Test;
import static Server.Era.I;
import static org.junit.jupiter.api.Assertions.*;
class CharacterCardTest {
//Test if the method parsRow is able to create a correct CharacterCard
@Test
void parsRow() {
CharacterCard characterCard = CharacterCard.parsRow("C;1;2;I;SHAMAN;3;4");
assertEquals(1, characterCard.getCardId());
assertEquals(2, characterCard.getForMinPlayer());
assertEquals(I, characterCard.getEra());
assertEquals(CharacterType.SHAMAN, characterCard.getCharacterType());
assertEquals(3, characterCard.getIconValue());
assertEquals(4, characterCard.getPrestigePoints());
}
//Tests if the method parsRow send an exception with the message "Not a character" when is given
//as an input the wrong type of card
@Test
void wrongTypeOfCard(){
Exception exception = assertThrows(LoadingCardsException.class, () -> CharacterCard.parsRow("B;1;2;I;3;1;SHAMANIC_RITUAL"));
String actualMessage = exception.getMessage();
assertTrue(actualMessage.contains("Not a character card"));
}
}

View File

@@ -0,0 +1,34 @@
package Server.Cards;
import Server.Utils.LoadingCardsException;
import org.junit.jupiter.api.Test;
import static Server.Era.I;
import static org.junit.jupiter.api.Assertions.*;
class EventCardTest {
//Test if the method parsRow is able to create a correct EventCard
@Test
void parsRow() {
EventCard eventCard = EventCard.parsRow("E;1;2;I;CAVE_PAINTINGS;3;4");
assertEquals(1, eventCard.getCardId());
assertEquals(2, eventCard.getForMinPlayer());
assertEquals(I, eventCard.getEra());
assertEquals(Event.CAVE_PAINTINGS, eventCard.getEvent());
assertEquals(3, eventCard.getFirstValue());
assertEquals(4, eventCard.getSecondValue());
}
//Tests if the method parsRow send an exception with the message "Not an EventCard" when is given
//as an input the wrong type of card
@Test
void wrongTypeOfCard(){
Exception exception = assertThrows(LoadingCardsException.class, () -> EventCard.parsRow("B;1;2;I;3;1;SHAMANIC_RITUAL"));
String actualMessage = exception.getMessage();
assertTrue(actualMessage.contains("Not an EventCard"));
}
}

View File

@@ -0,0 +1,178 @@
package Server;
import Server.Cards.Card;
import Server.Cards.CardDeck;
import Server.Cards.CharacterCard;
import Server.Cards.EventCard;
import Server.Utils.EventsManagerException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class EventsSolverTest {
static List<Player> players = new ArrayList<>();
//Creating 4 players with a predefined tribe
@BeforeAll
public static void setUp(){
BuildingManager buildingManager = new BuildingManager();
Player winner = new Player("Winner", TotemColor.YELLOW);
winner.addFood(10);
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;1;2;I;SHAMAN;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;2;2;I;SHAMAN;2;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;3;2;I;BUILDER;3;1"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;12;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;13;3;I;GATHERER;3;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;14;0;I;ARTIST;0;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;15;0;I;ARTIST;0;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;21;0;I;ARTIST;1;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;22;0;I;INVENTOR;9;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;23;4;I;INVENTOR;5;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;43;0;I;HUNTER;0;0"));
winner.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;44;3;I;HUNTER;0;0"));
players.add(winner);
Player medium = new Player("Medium", TotemColor.RED);
medium.addFood(10);
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;56;2;I;SHAMAN;3;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;34;2;I;BUILDER;3;1"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;32;3;I;GATHERER;3;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;33;0;I;ARTIST;0;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;45;3;I;HUNTER;0;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;72;0;I;ARTIST;9;0"));
medium.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;73;4;I;INVENTOR;5;0"));
players.add(medium);
Player loser = new Player("Loser", TotemColor.BLUE);
loser.addFood(2);
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;54;2;I;SHAMAN;2;0"));
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;36;2;I;BUILDER;3;1"));
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;82;0;I;INVENTOR;9;0"));
loser.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;93;4;I;INVENTOR;5;0"));
players.add(loser);
Player loser2 = new Player("Loser2", TotemColor.PURPLE);
loser2.addFood(2);
loser2.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;54;2;I;SHAMAN;2;0"));
loser2.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;36;2;I;BUILDER;3;1"));
loser2.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;82;0;I;ARTIST;9;0"));
loser2.getPlayerTribe().addCharacter(CharacterCard.parsRow("C;93;4;I;INVENTOR;5;0"));
players.add(loser2);
}
//Testing if solveEvents is able to execute a series of events
@Test
void solveEvents() {
CardDeck cardDeck = new CardDeck();
cardDeck.setForNPlayer("./src/test/resources/files/only_event_deck.csv", 5);
List<EventCard> events = new ArrayList<>();
for(Card c: cardDeck.getTribeDeck()){
events.add((EventCard) c);
}
assertTrue(EventsSolver.solveEvents(events, players));
}
//Testing if sustainment is giving the correct output after inserting a valid input
@Test
void sustainment() {
EventCard sustainment = EventCard.parsRow("E;1;0;I;SUSTAINMENT;2;0");
ArrayList<Integer> target = new ArrayList<>();
target.add(players.getFirst().getFoodTokens());
target.add(players.getFirst().getPrestigePoints());
target.add(players.get(1).getFoodTokens() - 4);
target.add(players.get(1).getPrestigePoints());
target.add(0);
target.add(players.get(3).getPrestigePoints()-8);
target.add(0);
target.add(players.get(3).getPrestigePoints()-8);
assertEquals(target, EventsSolver.sustainment(sustainment, players));
}
//Testing if hunt is giving the correct output after inserting a valid input
@Test
void hunt() {
EventCard hunt = EventCard.parsRow("E;1;0;I;HUNT;3;0");
ArrayList<Integer> target = new ArrayList<>();
target.add(players.getFirst().getFoodTokens() + 2);
target.add(players.getFirst().getPrestigePoints() + 6);
target.add(players.get(1).getFoodTokens() + 1);
target.add(players.get(1).getPrestigePoints() + 3);
target.add(players.get(2).getFoodTokens());
target.add(players.get(2).getPrestigePoints());
target.add(players.get(3).getFoodTokens());
target.add(players.get(3).getPrestigePoints());
assertEquals(target, EventsSolver.hunt(hunt, players));
}
//Testing if shamanicRitual is giving the correct output after inserting a valid input
@Test
void shamanicRitual() {
EventCard shamanicRitual = EventCard.parsRow("E;1;0;I;SHAMANIC_RITUAL;10;5");
ArrayList<Integer> target = new ArrayList<>();
target.add(players.getFirst().getPrestigePoints() + 10);
target.add(players.get(1).getPrestigePoints());
target.add(players.get(2).getPrestigePoints()-5);
target.add(players.get(3).getPrestigePoints()-5);
assertEquals(target, EventsSolver.shamanicRitual(shamanicRitual, players));
}
//Testing if cavePainting is giving the correct output after inserting a valid input
@Test
void cavePaintings() {
EventCard cavePainting = EventCard.parsRow("E;4;4;FINAL;CAVE_PAINTINGS;2;2");
ArrayList<Integer> target = new ArrayList<>();
target.add(players.getFirst().getPrestigePoints() + 6);
target.add(players.get(1).getPrestigePoints() + 4);
target.add(players.get(2).getPrestigePoints()-2);
target.add(players.get(3).getPrestigePoints()-2);
assertEquals(target, EventsSolver.cavePaintings(cavePainting, players));
}
@Test
void wrongInputs(){
EventCard cavePainting = EventCard.parsRow("E;4;4;FINAL;CAVE_PAINTINGS;2;2");
Exception sustainmentException = assertThrows(EventsManagerException.class, () -> EventsSolver.sustainment(cavePainting, players));
String SustainmentMessage = sustainmentException.getMessage();
assertTrue(SustainmentMessage.contains("Not a sustainment card"));
Exception shamanicException = assertThrows(EventsManagerException.class, () -> EventsSolver.shamanicRitual(cavePainting, players));
String shamanicMessage = shamanicException.getMessage();
assertTrue(shamanicMessage.contains("Not a shamanic ritual card"));
Exception huntException = assertThrows(EventsManagerException.class, () -> EventsSolver.hunt(cavePainting, players));
String huntMessage = huntException.getMessage();
assertTrue(huntMessage.contains("Not a hunt card"));
EventCard sustainment = EventCard.parsRow("E;1;0;I;SUSTAINMENT;2;0");
Exception cavePaintingException = assertThrows(EventsManagerException.class, () -> EventsSolver.cavePaintings(sustainment, players));
String cavePaintingMessage = cavePaintingException.getMessage();
assertTrue(cavePaintingMessage.contains("Not a cave painting card"));
}
}

View File

@@ -0,0 +1,134 @@
package Server;
import Server.Cards.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class GameBoardTest {
CardDeck deck = new CardDeck();
Player player1 = new Player("A", TotemColor.PURPLE);
Player player2 = new Player("B", TotemColor.YELLOW);
Player player3 = new Player("C", TotemColor.BLUE);
Player player4 = new Player("D", TotemColor.GREEN);
Player player5 = new Player("E", TotemColor.RED);
List<Player> players = new ArrayList<>();
@BeforeEach
void setUp() {
players.add(player1);
players.add(player2);
players.add(player3);
players.add(player4);
players.add(player5);
deck.setForNPlayer(".\\src\\test\\resources\\files\\test_gameboard.csv",5);
}
@Test
void setTotemOnTile() {
//tests that the tile is occupied
GameBoard gameBoard = new GameBoard(Era.I,deck, players.size());
Player player = new Player("Lucy", TotemColor.BLUE);
OfferingTile tile = new OfferingTile(4);
assertTrue(gameBoard.placeTotem(player,tile,gameBoard.getTurnTile() ));
}
@Test
void drawBottomCard() {
//test that a hunter card has been added to the player
CardDeck deck2 = new CardDeck();
deck2.setForNPlayer("./src/test/resources/files/test_gameboard_characters.csv",5);
GameBoard gameBoard = new GameBoard(Era.I,deck2,players.size());
Player player = new Player("Lucy", TotemColor.BLUE);
gameBoard.setupInitialRows(5);
// gameBoard.takeFromBottomRow(gameBoard.getBottomrow().get(2),player);
// assertEquals(1,player.getPlayerTribe().countCharactersByType(CharacterType.HUNTER));
}
@Test
void drawTopCard() {
//tests that a hunter card has been added to the player
CardDeck deck2 = new CardDeck();
deck2.setForNPlayer("./src/test/resources/files/test_gameboard_characters.csv",5);
GameBoard gameBoard = new GameBoard(Era.I,deck2,players.size());
Player player = new Player("Lucy", TotemColor.BLUE);
gameBoard.setupInitialRows(5);
//gameBoard.DrawTopCard(gameBoard.getToprow().get(2),player);
//assertEquals(1,player.getPlayerTribe().countCharactersByType(CharacterType.HUNTER));
}
@Test
void eventsToSolve() {
//tests that the result is a list of event cards
CardDeck deck2 = new CardDeck();
deck2.setForNPlayer(".\\src\\test\\resources\\files\\test_gameboard_events.csv",5);
GameBoard gameBoard = new GameBoard(Era.I,deck2,players.size());
gameBoard.setupInitialRows(5);
//assertTrue(gameBoard.EventsToSolve().getFirst().getCardId() > 84);
}
@Test
void setRows() {
//tests that the top row has n+4 cards
GameBoard gameBoard = new GameBoard(Era.I,deck,players.size());
//assertEquals(9,gameBoard.SetRows(5).size());
}
@Test
void setOffersToNull() {
//tests that the tiles are free
GameBoard gameBoard = new GameBoard(Era.I,deck,players.size());
// assertEquals(Collections.emptyList(),gameBoard.SetOffersToNull(5));
}
@Test
void nextEra() {
//tests that the eras progress
GameBoard gameBoard = new GameBoard(Era.I,deck,players.size());
// assertEquals(Era.II,gameBoard.NextEra());
}
@Test
void changeBuildings() {
//tests that with era change the top row of building is of era II
CardDeck deck2 = new CardDeck();
deck2.setForNPlayer(".\\src\\test\\resources\\files\\only_building_deck.csv",5);
GameBoard gameBoard = new GameBoard(Era.II,deck2,players.size());
//assertEquals(Era.II,gameBoard.ChangeBuildings().getFirst().getEra());
}
@Test
void getBuilding() {
//tests that the building has gained the player as owner
GameBoard gameBoard = new GameBoard(Era.I,deck,players.size());
Player player = new Player("Diego",TotemColor.BLUE);
player.addFood(4);
//BuildingCard target = new BuildingCard(111,0,Era.II,4,3, Trigger.ENDGAME_BONUS_POINTS,null);
//assertEquals(target.getOwner(),gameBoard.GetBuilding(player,target).getOwner());
}
}

View File

@@ -0,0 +1,64 @@
package Server;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class OfferingTileTest {
OfferingTile offeringTile;
@Test
void isEmpty() {
//test the ability of the method to recognize an occupied tile
OfferingTile offeringTile = new OfferingTile(4);
assertTrue(offeringTile.isEmpty());
}
@BeforeEach
public void init(){
offeringTile = new OfferingTile(4);
offeringTile.setOccupant(new Player("Johnny", TotemColor.BLUE));
}
@Test
void RemoveOccupant(){
//test that the occupant has been removed
assertNull(offeringTile.removeOccupant());
}
@Test
void GetOccupant(){
assertEquals("Johnny",offeringTile.getOccupant().getNickname());
}
@Test
void SetOccupant(){
//test that the player has been set
assertEquals("Johnny",offeringTile.getOccupant().getNickname());
}
@Test
void getActions(){
//tests that the actions associated to tile are the correct ones
OfferingTile offeringTile = new OfferingTile(4);
List<Symbol> correct = List.of(Symbol.DOWN,Symbol.UP);
assertEquals(correct,offeringTile.getActions());
}
}

View File

@@ -0,0 +1,99 @@
package Server;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class PlayerTest {
@Test
@DisplayName("Dovrebbe aggiungere la quantità di cibo indicata")
void shouldAddFood() {
//Arrange
Player player = new Player("Giovanni", TotemColor.YELLOW);
//Act
player.addFood(5);
player.addFood(2);
//Assert
assertEquals(7, player.getFoodTokens(), "5 + 2 deve fare 7");
}
@Test
@DisplayName("Dovrebbe sottrarre il cibo quando i fondi sono sufficienti")
void shouldSubtractFoodWhenFundsAreSufficient() {
//Arrange
Player player = new Player("Mario", TotemColor.PURPLE);
//Act
player.addFood(5);
player.removeFood(2);
//Assert
assertEquals(3, player.getFoodTokens());
}
@Test
@DisplayName("Non dovrebbe far scendere il cibo sotto zero quando i fondi sono insufficienti")
void shouldNotGoNegativeWhenFoodIsInsufficient() {
//Arrange
Player player = new Player("Luigi", TotemColor.YELLOW);
//Act
player.addFood(2);
player.removeFood(5);
//Assert
assertEquals(2, player.getFoodTokens()); //Se il cibo è insufficiente, il cibo rimane lo stesso di prima, non diminuisce!
}
@Test
@DisplayName("Dovrebbe sommare correttamente i Punti Prestigio aggiunti")
void shouldAddPrestigePoints() {
// Arrange
Player player = new Player("Yoshi", TotemColor.YELLOW);
// Act
player.addPrestigePoints(3);
player.addPrestigePoints(4);
player.addPrestigePoints(10);
// Assert
assertEquals(17, player.getPrestigePoints());
}
@Test
@DisplayName("Dovrebbe sottrarre correttamente i Punti Prestigio persi")
void shouldRemovePrestigePoints() {
//Arrange
Player player = new Player("Gianbruno", TotemColor.BLUE);
player.addPrestigePoints(10);
// Act
player.removePrestigePoints(4);
// Assert
assertEquals(6, player.getPrestigePoints());
}
@Test
@DisplayName("Dovrebbe sottrarre correttamente i Punti Prestigio persi")
void shouldRemovePrestigePoints2() {
//Arrange
Player player1 = new Player("Gino", TotemColor.BLUE);
Player player2 = new Player("Olivia", TotemColor.RED);
player1.addPrestigePoints(10);
player2.addPrestigePoints(2);
// Act
player1.removePrestigePoints(4);
player2.removePrestigePoints(7);
// Assert
assertEquals(6, player1.getPrestigePoints());
assertEquals(-5, player2.getPrestigePoints());
}
}

View File

@@ -0,0 +1,131 @@
package Server;
import Server.Cards.BuildingCard;
import Server.Cards.CharacterCard;
import Server.Cards.CharacterType;
import Server.Cards.Trigger;
import Server.Era;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
class TribeTest {
private Tribe tribe;
@BeforeEach
public void setUp(){
tribe = new Tribe(); // creiamo una tribù nuova prima di ogni test
}
@Test
public void testGathererDiscount() {
// 2 gatherer -> 6 cibi scontati
tribe.addCharacter(new CharacterCard(1, 2, Era.I, CharacterType.GATHERER, 3, 0));
tribe.addCharacter(new CharacterCard(2, 2, Era.I, CharacterType.GATHERER, 3, 0));
assertEquals(6,tribe.gathererDiscount());
}
@Test
public void testArtistsEndPoints() {
// 5 artisti = 2 coppie. 2 * 10 = 20 punti.
tribe.addCharacter(new CharacterCard(14, 2, Era.I, CharacterType.ARTIST, 0, 0));
tribe.addCharacter(new CharacterCard(15, 2, Era.I, CharacterType.ARTIST, 0, 0));
tribe.addCharacter(new CharacterCard(16, 2, Era.I, CharacterType.ARTIST, 0, 0));
tribe.addCharacter(new CharacterCard(17, 3, Era.I, CharacterType.ARTIST, 0, 0));
tribe.addCharacter(new CharacterCard(18, 4, Era.I, CharacterType.ARTIST, 0, 0));
assertEquals(20, tribe.artistsEndPoints());
}
@Test
public void testInventorsEndPoints() {
// 3 Inventori, ma solo 2 invenzioni diverse: 2 (amo da pesca) e 3 (corda)
// 3 * 2= 6 punti
tribe.addCharacter(new CharacterCard(19, 2, Era.I, CharacterType.INVENTOR, 2, 0)); //statuetta
tribe.addCharacter(new CharacterCard(53, 2, Era.II, CharacterType.INVENTOR, 2, 0)); //statuetta
tribe.addCharacter(new CharacterCard(20, 2, Era.I, CharacterType.INVENTOR, 0, 0)); // canoa
assertEquals(6, tribe.inventorsEndPoints());
}
@Test
public void testBuildingBonusPoints() {
// Aggiungiamo l'edificio con id 117 che dà 25 punti
BuildingCard card25 = new BuildingCard(117, 2, Era.III, 10, 0, Trigger.ENDGAME_BONUS_POINTS, null);
tribe.addBuilding(card25);
// Se la tribù è vuota, dovrebbe dare esattamente 25 punti
assertEquals(25, tribe.endPoints(), "L'edificio bonus dovrebbe dare 25 punti");
}
@Test
public void testComplexScenario() {
// Due Artisti (1 coppia = 10 punti)
tribe.addCharacter(new CharacterCard(14, 2, Era.I, CharacterType.ARTIST, 0, 0));
tribe.addCharacter(new CharacterCard(15, 2, Era.I, CharacterType.ARTIST, 0, 0));
// Un costruttore che vale 5 punti prestigio base
tribe.addCharacter(new CharacterCard(62, 2, Era.II, CharacterType.BUILDER, 1, 5));
// L'edificio che raddoppia i punti dei costruttori (card id: 107 - ENDGAME_BUILDER_BONUS)
BuildingCard doubleBuilderPoints = new BuildingCard(107, 2, Era.II, 6, 4, Trigger.ENDGAME_BUILDER_BONUS, null);
tribe.addBuilding(doubleBuilderPoints);
/* CALCOLO ATTESO:
- Artisti: 10 punti
- Costruttori base: 5 punti
- Bonus buildingAbilitiesEndPoints (buildersEndPoints * 2): 5 * 2 = 10 punti
- Buildings: 4 punti
TOTALE: 29 punti
*/
assertEquals(29, tribe.endPoints(), "Il totale dovrebbe essere 29");
}
@Test
public void testComplexScenario2() {
// PERSONAGGI
// 3 Artisti (1 coppia = 10 punti)
tribe.addCharacter(new CharacterCard(14, 2, Era.I, CharacterType.ARTIST, 0, 0));
tribe.addCharacter(new CharacterCard(15, 2, Era.I, CharacterType.ARTIST, 0, 0));
tribe.addCharacter(new CharacterCard(16, 2, Era.I, CharacterType.ARTIST, 0, 0));
// 2 Costruttori (7 punti base totali)
tribe.addCharacter(new CharacterCard(6, 2, Era.I, CharacterType.BUILDER, 1, 3));
tribe.addCharacter(new CharacterCard(36, 2, Era.II, CharacterType.BUILDER, 1, 4));
// 4 Inventori (2 del gruppo 1, 1 del gruppo 2, 1 del gruppo 3 -> 4*3 = 12 punti)
tribe.addCharacter(new CharacterCard(48, 2, Era.II, CharacterType.INVENTOR, 2, 0)); // Statuetta (iconValue=2)
tribe.addCharacter(new CharacterCard(76, 2, Era.III, CharacterType.INVENTOR, 2, 0)); // Statuetta
tribe.addCharacter(new CharacterCard(49, 4, Era.II, CharacterType.INVENTOR, 3, 0)); // Amo da pesca
tribe.addCharacter(new CharacterCard(23, 4, Era.I, CharacterType.INVENTOR, 5, 0)); // Corda
// Rimanenti per fare il set da 6
tribe.addCharacter(new CharacterCard(10, 2, Era.I, CharacterType.GATHERER, 3, 0));
tribe.addCharacter(new CharacterCard(11, 2, Era.I, CharacterType.GATHERER, 3, 0));
tribe.addCharacter(new CharacterCard(1, 2, Era.I, CharacterType.HUNTER, 1, 0));
tribe.addCharacter(new CharacterCard(27, 2, Era.I, CharacterType.SHAMAN, 2, 0));
// EDIFICI
// id 107 (Bonus Costruttori) - 4 Punti Base Edificio
tribe.addBuilding(new BuildingCard(107, 2, Era.II, 6, 4, Trigger.ENDGAME_BUILDER_BONUS, null));
// id 109 (Set da 6) - 6 Punti Base Edificio
tribe.addBuilding(new BuildingCard(109, 2, Era.II, 5, 6, Trigger.ENDGAME_FOR_SIX, null));
// id 112 (Bonus Sciamani) - 4 Punti Base Edificio
tribe.addBuilding(new BuildingCard(112, 2, Era.III, 7, 4, Trigger.ENDGAME_BONUS_CHARACTER, null));
// id 117 (25 Punti) - 0 Punti Base Edificio
tribe.addBuilding(new BuildingCard(117, 2, Era.III, 10, 0, Trigger.ENDGAME_BONUS_POINTS, null));
// VERIFICA
// Totale atteso = 92
assertEquals(92, tribe.endPoints(), "Il totale dovrebbe essere 92 punti");
}
}

View File

@@ -0,0 +1,62 @@
package Server;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class TurnTileOldTest {
TurnTileOld turnTileOld;
Player player1 = new Player("A", TotemColor.PURPLE);
Player player2 = new Player("B", TotemColor.YELLOW);
Player player3 = new Player("C", TotemColor.BLUE);
Player player4 = new Player("D", TotemColor.GREEN);
Player player5 = new Player("E", TotemColor.RED);
List<Player> players = new ArrayList<>();
@BeforeEach
public void setUp(){
players.add(player1);
players.add(player2);
players.add(player3);
players.add(player4);
players.add(player5);
turnTileOld = new TurnTileOld(5);
}
@Test
void startOrder() {
assertEquals(5, turnTileOld.startOrder(players).length);
}
@Test
void giveReward() {
turnTileOld.startOrder(players);
player5.addFood(1);
assertEquals(0, turnTileOld.giveReward(player5).getFoodTokens());
}
@Test
void nextPlayer() {
//test that the method proceeds along the array
turnTileOld.startOrder(players);
assertEquals(player2, turnTileOld.nextPlayer());
}
@Test
void addPlayer() {
//verify that the first player changes
turnTileOld.startOrder(players);
assertEquals(player2, turnTileOld.addPlayer(player2));
}
}

View File

@@ -0,0 +1,64 @@
package Server;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
class TurnTileTest {
TurnTile turnTile;
Player player1 = new Player("A", TotemColor.PURPLE);
Player player2 = new Player("B", TotemColor.YELLOW);
Player player3 = new Player("C", TotemColor.BLUE);
Player player4 = new Player("D", TotemColor.GREEN);
Player player5 = new Player("E", TotemColor.RED);
List<Player> players = new ArrayList<>();
@BeforeEach
public void setUp(){
players.add(player1);
players.add(player2);
players.add(player3);
players.add(player4);
players.add(player5);
turnTile = new TurnTile(5);
}
/**
@Test
void startOrder() {
assertEquals(5, turnTileOld.startOrder(players).length);
}
@Test
void giveReward() {
turnTileOld.startOrder(players);
player5.addFood(1);
assertEquals(0, turnTileOld.giveReward(player5).getFoodTokens());
}
@Test
void nextPlayer() {
//test that the method proceeds along the array
turnTileOld.startOrder(players);
assertEquals(player2, turnTileOld.nextPlayer());
}
@Test
void addPlayer() {
//verify that the first player changes
turnTileOld.startOrder(players);
assertEquals(player2, turnTileOld.addPlayer(player2));
}
**/
}

View File

@@ -0,0 +1,17 @@
B;1;2;I;3;1;SHAMAN_NO_LOSS
B;3;4;II;6;2;FOOD_FOR_SIX
B;3;5;II;6;2;SUSTAIN_DISCOUNT;INVENTOR
B;17;5;II;6;2;SUSTAIN_DISCOUNT;ARTIST
B;5;3;III;6;2;SHAMAN_NO_LOSS
B;6;3;II;6;2;BONUS_FOOD_ENDTURN
B;7;3;II;6;2;FOOD_PER_INVENTORS
B;8;3;II;6;2;SHAMAN_BONUS
B;9;3;II;6;2;SHAMAN_DOUBLE_POINTS
B;10;3;II;6;2;HUNT_BONUS
B;11;3;II;6;2;ENDGAME_BUILDER_BONUS
B;12;3;II;6;2;PAINTING_FOOD_BONUS
B;13;3;II;6;2;ENDGAME_FOR_SIX
B;14;3;II;6;2;ENDGAME_BONUS_CHARACTER;BUILDER
B;18;3;II;6;2;ENDGAME_BONUS_CHARACTER;SHAMAN
B;15;3;II;6;2;EXTRA_DRAW
B;16;3;II;6;2;ENDGAME_BONUS_POINTS
1 B;1;2;I;3;1;SHAMAN_NO_LOSS
2 B;3;4;II;6;2;FOOD_FOR_SIX
3 B;3;5;II;6;2;SUSTAIN_DISCOUNT;INVENTOR
4 B;17;5;II;6;2;SUSTAIN_DISCOUNT;ARTIST
5 B;5;3;III;6;2;SHAMAN_NO_LOSS
6 B;6;3;II;6;2;BONUS_FOOD_ENDTURN
7 B;7;3;II;6;2;FOOD_PER_INVENTORS
8 B;8;3;II;6;2;SHAMAN_BONUS
9 B;9;3;II;6;2;SHAMAN_DOUBLE_POINTS
10 B;10;3;II;6;2;HUNT_BONUS
11 B;11;3;II;6;2;ENDGAME_BUILDER_BONUS
12 B;12;3;II;6;2;PAINTING_FOOD_BONUS
13 B;13;3;II;6;2;ENDGAME_FOR_SIX
14 B;14;3;II;6;2;ENDGAME_BONUS_CHARACTER;BUILDER
15 B;18;3;II;6;2;ENDGAME_BONUS_CHARACTER;SHAMAN
16 B;15;3;II;6;2;EXTRA_DRAW
17 B;16;3;II;6;2;ENDGAME_BONUS_POINTS

View File

@@ -0,0 +1,3 @@
C;24;3;I;INVENTOR;7;0
C;25;4;I;INVENTOR;4;0
C;26;5;II;SHAMAN;2;0
1 C 24 3 I INVENTOR 7 0
2 C 25 4 I INVENTOR 4 0
3 C 26 5 II SHAMAN 2 0

View File

@@ -0,0 +1,4 @@
E;1;0;I;SUSTAINMENT;1;0
E;2;5;II;HUNT;2;0
E;3;3;III;SHAMANIC_RITUAL;2;3
E;4;4;FINAL;CAVE_PAINTINGS;2;1
1 E 1 0 I SUSTAINMENT 1 0
2 E 2 5 II HUNT 2 0
3 E 3 3 III SHAMANIC_RITUAL 2 3
4 E 4 4 FINAL CAVE_PAINTINGS 2 1

View File

@@ -0,0 +1,24 @@
B;1;2;I;3;1;SHAMAN_NO_LOSS
B;3;4;II;6;2;FOOD_FOR_SIX
B;3;5;II;6;2;SUSTAIN_DISCOUNT;INVENTOR
B;17;5;II;6;2;SUSTAIN_DISCOUNT;ARTIST
B;5;3;III;6;2;SHAMAN_NO_LOSS
B;6;3;II;6;2;BONUS_FOOD_ENDTURN
B;7;3;II;6;2;FOOD_PER_INVENTORS
B;8;3;II;6;2;SHAMAN_BONUS
B;9;3;II;6;2;SHAMAN_DOUBLE_POINTS
B;10;3;II;6;2;HUNT_BONUS
B;11;3;II;6;2;ENDGAME_BUILDER_BONUS
B;12;3;II;6;2;PAINTING_FOOD_BONUS
B;13;3;II;6;2;ENDGAME_FOR_SIX
B;14;3;II;6;2;ENDGAME_BONUS_CHARACTER;BUILDER
B;18;3;II;6;2;ENDGAME_BONUS_CHARACTER;SHAMAN
B;15;3;II;6;2;EXTRA_DRAW
B;16;3;II;6;2;ENDGAME_BONUS_POINTS
C;24;3;I;INVENTOR;7;0
C;25;4;I;INVENTOR;4;0
C;26;5;II;SHAMAN;2;0
E;21;0;I;SUSTAINMENT;1;0
E;22;5;II;HUNT;2;0
E;23;3;III;SHAMANIC_RITUAL;2;3
E;34;4;FINAL;CAVE_PAINTINGS;2;1
1 B;1;2;I;3;1;SHAMAN_NO_LOSS
2 B;3;4;II;6;2;FOOD_FOR_SIX
3 B;3;5;II;6;2;SUSTAIN_DISCOUNT;INVENTOR
4 B;17;5;II;6;2;SUSTAIN_DISCOUNT;ARTIST
5 B;5;3;III;6;2;SHAMAN_NO_LOSS
6 B;6;3;II;6;2;BONUS_FOOD_ENDTURN
7 B;7;3;II;6;2;FOOD_PER_INVENTORS
8 B;8;3;II;6;2;SHAMAN_BONUS
9 B;9;3;II;6;2;SHAMAN_DOUBLE_POINTS
10 B;10;3;II;6;2;HUNT_BONUS
11 B;11;3;II;6;2;ENDGAME_BUILDER_BONUS
12 B;12;3;II;6;2;PAINTING_FOOD_BONUS
13 B;13;3;II;6;2;ENDGAME_FOR_SIX
14 B;14;3;II;6;2;ENDGAME_BONUS_CHARACTER;BUILDER
15 B;18;3;II;6;2;ENDGAME_BONUS_CHARACTER;SHAMAN
16 B;15;3;II;6;2;EXTRA_DRAW
17 B;16;3;II;6;2;ENDGAME_BONUS_POINTS
18 C;24;3;I;INVENTOR;7;0
19 C;25;4;I;INVENTOR;4;0
20 C;26;5;II;SHAMAN;2;0
21 E;21;0;I;SUSTAINMENT;1;0
22 E;22;5;II;HUNT;2;0
23 E;23;3;III;SHAMANIC_RITUAL;2;3
24 E;34;4;FINAL;CAVE_PAINTINGS;2;1

View File

@@ -0,0 +1,96 @@
C;1;0;I;HUNTER;1;0
C;2;0;I;HUNTER;1;0
C;3;0;I;HUNTER;0;0
C;4;3;I;HUNTER;0;0
C;5;3;I;HUNTER;0;0
C;6;0;I;HUNTER;1;3
C;7;0;I;HUNTER;2;0
C;8;5;I;HUNTER;2;1
C;9;0;I;HUNTER;1;2
C;10;0;I;HUNTER;3;0
C;11;0;I;HUNTER;3;0
C;12;3;I;HUNTER;3;0
C;13;5;I;HUNTER;3;0
C;14;0;I;HUNTER;0;0
C;15;0;I;HUNTER;0;0
C;16;0;I;HUNTER;0;0
C;17;3;I;HUNTER;0;0
C;18;4;I;HUNTER;0;0
C;19;0;I;HUNTER;8;0
C;20;0;I;HUNTER;0;0
C;21;0;I;HUNTER;1;0
C;22;0;I;HUNTER;9;0
C;23;4;I;HUNTER;5;0
C;24;4;I;HUNTER;7;0
C;25;4;I;HUNTER;4;0
C;26;5;I;HUNTER;2;0
C;27;0;I;HUNTER;2;0
C;28;0;I;HUNTER;1;0
C;29;4;I;HUNTER;1;0
C;30;0;II;HUNTER;0;0
C;31;0;II;HUNTER;0;0
C;32;3;II;HUNTER;1;0
C;33;0;II;HUNTER;1;0
C;34;4;II;HUNTER;1;0
C;35;5;II;HUNTER;0;0
C;36;0;II;HUNTER;1;4
C;37;0;II;HUNTER;2;1
C;38;3;II;HUNTER;1;2
C;39;0;II;HUNTER;2;3
C;40;0;II;HUNTER;3;0
C;41;3;II;HUNTER;3;0
C;42;4;II;HUNTER;3;0
C;43;5;II;HUNTER;3;0
C;44;3;II;HUNTER;0;0
C;45;0;II;HUNTER;0;0
C;46;0;II;HUNTER;0;0
C;47;0;II;HUNTER;0;0
C;48;0;II;HUNTER;0;0
C;49;4;II;HUNTER;0;0
C;50;0;II;HUNTER;0;0
C;51;0;II;HUNTER;0;0
C;52;0;II;HUNTER;0;0
C;53;0;II;HUNTER;0;0
C;54;0;II;HUNTER;2;0
C;55;0;II;HUNTER;2;0
C;56;5;II;HUNTER;1;0
C;57;5;II;HUNTER;2;0
C;58;5;III;HUNTER;1;0
C;59;0;III;HUNTER;0;0
C;60;0;III;HUNTER;0;0
C;61;0;III;HUNTER;1;0
C;62;0;III;HUNTER;1;5
C;63;0;III;HUNTER;2;3
C;64;5;III;HUNTER;1;4
C;65;0;III;HUNTER;2;2
C;66;5;III;HUNTER;3;0
C;67;4;III;HUNTER;3;0
C;68;0;III;HUNTER;3;0
C;69;5;III;HUNTER;0;0
C;70;0;III;HUNTER;0;0
C;71;0;III;HUNTER;0;0
C;72;0;III;HUNTER;0;0
C;73;4;III;HUNTER;0;0
C;74;3;III;HUNTER;0;0
C;75;3;III;HUNTER;0;0
C;76;0;III;HUNTER;0;0
C;77;0;III;HUNTER;0;0
C;78;0;III;HUNTER;0;0
C;79;0;III;HUNTER;0;0
C;80;3;III;HUNTER;2;0
C;81;0;III;HUNTER;3;0
C;82;0;III;HUNTER;2;0
C;83;0;III;HUNTER;3;0
C;84;4;III;HUNTER;2;0
E;85;0;I;HUNT;1;1
E;86;0;I;SUSTAINMENT;1;1
E;87;0;I;SHAMANIC_RITUAL;5;3
E;88;0;I;CAVE_PAINTINGS;2;1
E;89;0;II;HUNT;1;2
E;90;0;II;SUSTAINMENT;1;2
E;91;0;II;SHAMANIC_RITUAL;10;5
E;92;0;II;CAVE_PAINTINGS;2;2
E;93;0;III;HUNT;1;3
E;94;0;III;CAVE_PAINTINGS;2;3
E;95;0;FINAL;SUSTAINMENT;1;3
E;96;0;FINAL;SHAMANIC_RITUAL;15;7
1 C 1 0 I HUNTER 1 0
2 C 2 0 I HUNTER 1 0
3 C 3 0 I HUNTER 0 0
4 C 4 3 I HUNTER 0 0
5 C 5 3 I HUNTER 0 0
6 C 6 0 I HUNTER 1 3
7 C 7 0 I HUNTER 2 0
8 C 8 5 I HUNTER 2 1
9 C 9 0 I HUNTER 1 2
10 C 10 0 I HUNTER 3 0
11 C 11 0 I HUNTER 3 0
12 C 12 3 I HUNTER 3 0
13 C 13 5 I HUNTER 3 0
14 C 14 0 I HUNTER 0 0
15 C 15 0 I HUNTER 0 0
16 C 16 0 I HUNTER 0 0
17 C 17 3 I HUNTER 0 0
18 C 18 4 I HUNTER 0 0
19 C 19 0 I HUNTER 8 0
20 C 20 0 I HUNTER 0 0
21 C 21 0 I HUNTER 1 0
22 C 22 0 I HUNTER 9 0
23 C 23 4 I HUNTER 5 0
24 C 24 4 I HUNTER 7 0
25 C 25 4 I HUNTER 4 0
26 C 26 5 I HUNTER 2 0
27 C 27 0 I HUNTER 2 0
28 C 28 0 I HUNTER 1 0
29 C 29 4 I HUNTER 1 0
30 C 30 0 II HUNTER 0 0
31 C 31 0 II HUNTER 0 0
32 C 32 3 II HUNTER 1 0
33 C 33 0 II HUNTER 1 0
34 C 34 4 II HUNTER 1 0
35 C 35 5 II HUNTER 0 0
36 C 36 0 II HUNTER 1 4
37 C 37 0 II HUNTER 2 1
38 C 38 3 II HUNTER 1 2
39 C 39 0 II HUNTER 2 3
40 C 40 0 II HUNTER 3 0
41 C 41 3 II HUNTER 3 0
42 C 42 4 II HUNTER 3 0
43 C 43 5 II HUNTER 3 0
44 C 44 3 II HUNTER 0 0
45 C 45 0 II HUNTER 0 0
46 C 46 0 II HUNTER 0 0
47 C 47 0 II HUNTER 0 0
48 C 48 0 II HUNTER 0 0
49 C 49 4 II HUNTER 0 0
50 C 50 0 II HUNTER 0 0
51 C 51 0 II HUNTER 0 0
52 C 52 0 II HUNTER 0 0
53 C 53 0 II HUNTER 0 0
54 C 54 0 II HUNTER 2 0
55 C 55 0 II HUNTER 2 0
56 C 56 5 II HUNTER 1 0
57 C 57 5 II HUNTER 2 0
58 C 58 5 III HUNTER 1 0
59 C 59 0 III HUNTER 0 0
60 C 60 0 III HUNTER 0 0
61 C 61 0 III HUNTER 1 0
62 C 62 0 III HUNTER 1 5
63 C 63 0 III HUNTER 2 3
64 C 64 5 III HUNTER 1 4
65 C 65 0 III HUNTER 2 2
66 C 66 5 III HUNTER 3 0
67 C 67 4 III HUNTER 3 0
68 C 68 0 III HUNTER 3 0
69 C 69 5 III HUNTER 0 0
70 C 70 0 III HUNTER 0 0
71 C 71 0 III HUNTER 0 0
72 C 72 0 III HUNTER 0 0
73 C 73 4 III HUNTER 0 0
74 C 74 3 III HUNTER 0 0
75 C 75 3 III HUNTER 0 0
76 C 76 0 III HUNTER 0 0
77 C 77 0 III HUNTER 0 0
78 C 78 0 III HUNTER 0 0
79 C 79 0 III HUNTER 0 0
80 C 80 3 III HUNTER 2 0
81 C 81 0 III HUNTER 3 0
82 C 82 0 III HUNTER 2 0
83 C 83 0 III HUNTER 3 0
84 C 84 4 III HUNTER 2 0
85 E 85 0 I HUNT 1 1
86 E 86 0 I SUSTAINMENT 1 1
87 E 87 0 I SHAMANIC_RITUAL 5 3
88 E 88 0 I CAVE_PAINTINGS 2 1
89 E 89 0 II HUNT 1 2
90 E 90 0 II SUSTAINMENT 1 2
91 E 91 0 II SHAMANIC_RITUAL 10 5
92 E 92 0 II CAVE_PAINTINGS 2 2
93 E 93 0 III HUNT 1 3
94 E 94 0 III CAVE_PAINTINGS 2 3
95 E 95 0 FINAL SUSTAINMENT 1 3
96 E 96 0 FINAL SHAMANIC_RITUAL 15 7

View File

@@ -0,0 +1,84 @@
C;1;0;I;HUNTER;1;0
C;2;0;I;HUNTER;1;0
C;3;0;I;HUNTER;0;0
C;4;3;I;HUNTER;0;0
C;5;3;I;HUNTER;0;0
C;6;0;I;HUNTER;1;3
C;7;0;I;HUNTER;2;0
C;8;5;I;HUNTER;2;1
C;9;0;I;HUNTER;1;2
C;10;0;I;HUNTER;3;0
C;11;0;I;HUNTER;3;0
C;12;3;I;HUNTER;3;0
C;13;5;I;HUNTER;3;0
C;14;0;I;HUNTER;0;0
C;15;0;I;HUNTER;0;0
C;16;0;I;HUNTER;0;0
C;17;3;I;HUNTER;0;0
C;18;4;I;HUNTER;0;0
C;19;0;I;HUNTER;8;0
C;20;0;I;HUNTER;0;0
C;21;0;I;HUNTER;1;0
C;22;0;I;HUNTER;9;0
C;23;4;I;HUNTER;5;0
C;24;4;I;HUNTER;7;0
C;25;4;I;HUNTER;4;0
C;26;5;I;HUNTER;2;0
C;27;0;I;HUNTER;2;0
C;28;0;I;HUNTER;1;0
C;29;4;I;HUNTER;1;0
C;30;0;II;HUNTER;0;0
C;31;0;II;HUNTER;0;0
C;32;3;II;HUNTER;1;0
C;33;0;II;HUNTER;1;0
C;34;4;II;HUNTER;1;0
C;35;5;II;HUNTER;0;0
C;36;0;II;HUNTER;1;4
C;37;0;II;HUNTER;2;1
C;38;3;II;HUNTER;1;2
C;39;0;II;HUNTER;2;3
C;40;0;II;HUNTER;3;0
C;41;3;II;HUNTER;3;0
C;42;4;II;HUNTER;3;0
C;43;5;II;HUNTER;3;0
C;44;3;II;HUNTER;0;0
C;45;0;II;HUNTER;0;0
C;46;0;II;HUNTER;0;0
C;47;0;II;HUNTER;0;0
C;48;0;II;HUNTER;0;0
C;49;4;II;HUNTER;0;0
C;50;0;II;HUNTER;0;0
C;51;0;II;HUNTER;0;0
C;52;0;II;HUNTER;0;0
C;53;0;II;HUNTER;0;0
C;54;0;II;HUNTER;2;0
C;55;0;II;HUNTER;2;0
C;56;5;II;HUNTER;1;0
C;57;5;II;HUNTER;2;0
C;58;5;III;HUNTER;1;0
C;59;0;III;HUNTER;0;0
C;60;0;III;HUNTER;0;0
C;61;0;III;HUNTER;1;0
C;62;0;III;HUNTER;1;5
C;63;0;III;HUNTER;2;3
C;64;5;III;HUNTER;1;4
C;65;0;III;HUNTER;2;2
C;66;5;III;HUNTER;3;0
C;67;4;III;HUNTER;3;0
C;68;0;III;HUNTER;3;0
C;69;5;III;HUNTER;0;0
C;70;0;III;HUNTER;0;0
C;71;0;III;HUNTER;0;0
C;72;0;III;HUNTER;0;0
C;73;4;III;HUNTER;0;0
C;74;3;III;HUNTER;0;0
C;75;3;III;HUNTER;0;0
C;76;0;III;HUNTER;0;0
C;77;0;III;HUNTER;0;0
C;78;0;III;HUNTER;0;0
C;79;0;III;HUNTER;0;0
C;80;3;III;HUNTER;2;0
C;81;0;III;HUNTER;3;0
C;82;0;III;HUNTER;2;0
C;83;0;III;HUNTER;3;0
C;84;4;III;HUNTER;2;0
1 C 1 0 I HUNTER 1 0
2 C 2 0 I HUNTER 1 0
3 C 3 0 I HUNTER 0 0
4 C 4 3 I HUNTER 0 0
5 C 5 3 I HUNTER 0 0
6 C 6 0 I HUNTER 1 3
7 C 7 0 I HUNTER 2 0
8 C 8 5 I HUNTER 2 1
9 C 9 0 I HUNTER 1 2
10 C 10 0 I HUNTER 3 0
11 C 11 0 I HUNTER 3 0
12 C 12 3 I HUNTER 3 0
13 C 13 5 I HUNTER 3 0
14 C 14 0 I HUNTER 0 0
15 C 15 0 I HUNTER 0 0
16 C 16 0 I HUNTER 0 0
17 C 17 3 I HUNTER 0 0
18 C 18 4 I HUNTER 0 0
19 C 19 0 I HUNTER 8 0
20 C 20 0 I HUNTER 0 0
21 C 21 0 I HUNTER 1 0
22 C 22 0 I HUNTER 9 0
23 C 23 4 I HUNTER 5 0
24 C 24 4 I HUNTER 7 0
25 C 25 4 I HUNTER 4 0
26 C 26 5 I HUNTER 2 0
27 C 27 0 I HUNTER 2 0
28 C 28 0 I HUNTER 1 0
29 C 29 4 I HUNTER 1 0
30 C 30 0 II HUNTER 0 0
31 C 31 0 II HUNTER 0 0
32 C 32 3 II HUNTER 1 0
33 C 33 0 II HUNTER 1 0
34 C 34 4 II HUNTER 1 0
35 C 35 5 II HUNTER 0 0
36 C 36 0 II HUNTER 1 4
37 C 37 0 II HUNTER 2 1
38 C 38 3 II HUNTER 1 2
39 C 39 0 II HUNTER 2 3
40 C 40 0 II HUNTER 3 0
41 C 41 3 II HUNTER 3 0
42 C 42 4 II HUNTER 3 0
43 C 43 5 II HUNTER 3 0
44 C 44 3 II HUNTER 0 0
45 C 45 0 II HUNTER 0 0
46 C 46 0 II HUNTER 0 0
47 C 47 0 II HUNTER 0 0
48 C 48 0 II HUNTER 0 0
49 C 49 4 II HUNTER 0 0
50 C 50 0 II HUNTER 0 0
51 C 51 0 II HUNTER 0 0
52 C 52 0 II HUNTER 0 0
53 C 53 0 II HUNTER 0 0
54 C 54 0 II HUNTER 2 0
55 C 55 0 II HUNTER 2 0
56 C 56 5 II HUNTER 1 0
57 C 57 5 II HUNTER 2 0
58 C 58 5 III HUNTER 1 0
59 C 59 0 III HUNTER 0 0
60 C 60 0 III HUNTER 0 0
61 C 61 0 III HUNTER 1 0
62 C 62 0 III HUNTER 1 5
63 C 63 0 III HUNTER 2 3
64 C 64 5 III HUNTER 1 4
65 C 65 0 III HUNTER 2 2
66 C 66 5 III HUNTER 3 0
67 C 67 4 III HUNTER 3 0
68 C 68 0 III HUNTER 3 0
69 C 69 5 III HUNTER 0 0
70 C 70 0 III HUNTER 0 0
71 C 71 0 III HUNTER 0 0
72 C 72 0 III HUNTER 0 0
73 C 73 4 III HUNTER 0 0
74 C 74 3 III HUNTER 0 0
75 C 75 3 III HUNTER 0 0
76 C 76 0 III HUNTER 0 0
77 C 77 0 III HUNTER 0 0
78 C 78 0 III HUNTER 0 0
79 C 79 0 III HUNTER 0 0
80 C 80 3 III HUNTER 2 0
81 C 81 0 III HUNTER 3 0
82 C 82 0 III HUNTER 2 0
83 C 83 0 III HUNTER 3 0
84 C 84 4 III HUNTER 2 0

View File

@@ -0,0 +1,12 @@
E;85;0;I;HUNT;1;1
E;86;0;I;SUSTAINMENT;1;1
E;87;0;I;SHAMANIC_RITUAL;5;3
E;88;0;I;CAVE_PAINTINGS;2;1
E;89;0;II;HUNT;1;2
E;90;0;II;SUSTAINMENT;1;2
E;91;0;II;SHAMANIC_RITUAL;10;5
E;92;0;II;CAVE_PAINTINGS;2;2
E;93;0;III;HUNT;1;3
E;94;0;III;CAVE_PAINTINGS;2;3
E;95;0;FINAL;SUSTAINMENT;1;3
E;96;0;FINAL;SHAMANIC_RITUAL;15;7
1 E 85 0 I HUNT 1 1
2 E 86 0 I SUSTAINMENT 1 1
3 E 87 0 I SHAMANIC_RITUAL 5 3
4 E 88 0 I CAVE_PAINTINGS 2 1
5 E 89 0 II HUNT 1 2
6 E 90 0 II SUSTAINMENT 1 2
7 E 91 0 II SHAMANIC_RITUAL 10 5
8 E 92 0 II CAVE_PAINTINGS 2 2
9 E 93 0 III HUNT 1 3
10 E 94 0 III CAVE_PAINTINGS 2 3
11 E 95 0 FINAL SUSTAINMENT 1 3
12 E 96 0 FINAL SHAMANIC_RITUAL 15 7