Quanzhi Fu, Yiheng Liu, Tianji Qiang, Ziyi Yang, Guowang Zeng
The Java RISK Game Project is a five-person team project based on Java to replicate the classic strategy game RISK. The project implements the core functions and game rules of the original RISK game through the Java language, while optimizing the game experience and providing a user-friendly interface. This project can be used as a practical case study for learning Java language programming, object-oriented programming, and game development.
- Java11
- Gradle 7.3.3
- Springboot 2.7.10
- Jackson 2.13
- HttpClient 4.5.13
build connection with the server
POST /start/
Parameter Name | Type | Comments |
---|---|---|
playerId | int | Player's Identity |
territories | []Territory | List of Territories |
lose | boolean | true if the player lose the game |
end | boolean | true if the game is over |
unitAvailable | int | units available for the player to place at his/her initial territories |
{
"playerId": 1,
"territories" : [
{
"Name": "A",
"TerritoryId" : 0,
"Owner" : 1,
"UnitNum" : 0,
"Distance" : [0, 1, 2]
},
{
"Name" : "B",
"TerritoryId" : 1,
"Owner" : 2,
"UnitNum" : 0,
"Distance" : [1, 0, 1]
},
{
"Name" : "C",
"TerritoryId" : 2,
"Owner" : 0,
"UnitNum" : 20086,
"Distance" : [2, 1, 0]
}
],
"lose": false,
"end": false,
"unitAvailable": 50
}
*The Territory object contains:
Parameter name | Type | Comments |
---|---|---|
Name | string | Name of the territory |
TerritoryId | int | Unique Identity of the territory |
Owner | int | The player id which this territory belongs to |
UnitNum | int | Number of Units in this Territory |
Distance | []int | Distance to other territories. Distance[i] indicate the distance toward territory with id i. |
place units in the territories
POST /place/
Parameter name | Type | Comments |
---|---|---|
PlayerId | int | Player's identity |
Placement | int[] | Placement[i] denotes the number of units the plaer deployed on territory with id i. The territory not belongs to the player will be set to -1. |
{
"PlayerId": 0,
"Placement": [
-1,
20,
30,
50,
-1,
-1
]
}
Same as start stage but without unitAvailable
Game execution phase, send command to server and print view to the player
POST /act/
Parameter name | type | comments |
---|---|---|
Player ID | int | Player's Identity |
MoveFrom | []int | src territories ids for move action |
MoveTo | []int | des territories ids for move action |
MoveNums | []int | num of units for move action |
AttackFrom | []int | src territories ids for attack action |
AttackTo | []int | des territories ids for attack action |
AttackNums | []int | num of units for attack action |
{
"PlayerId": 1,
"MoveFrom" : [
0,
0,
1
],
"MoveTo" : [
1,
2,
3
],
"MoveNums": [
10,
20,
30
],
"AttackFrom" : [
0,
0,
1
],
"AttackTo" : [
1,
2,
3
],
"AttackNums": [
10,
20,
30
]
}
Same as start stage but without unitAvailable
- Ask the player to type in username and password and send it to server
- The server will register for the player if the username is new.
- Otherwise, the password will be checked and the player will be required to type again if the password fails to match the username.
POST /login/
Parameter name | type | comments |
---|---|---|
username | String | username, unique for one player |
password | String | password of for that username |
{
"username": "tenkitenki",
"password": "riscrisc123"
}
A string indicating success or failure of login.
- First, the player will be given with all the game room available for him/her (past games that is not over or a new game), queried by a GET request
GET /roomid/
Parameter name | type | comments |
---|---|---|
username | String | username, unique for one player |
{
"username": "tenkitenki"
}
Parameter name | type | comments |
---|---|---|
roomids | []int | roomid available to join for the player |
{
"roomids" : [
10010,
32792,
8787
]
}
- Ask the player to type in the room ID to join a new game or return to a old game
- The player could also choose to exit the game here
- For the room ID,the back-end may be changed by:
- The server will hold a hashMap with room ID corresponding to a request handler, which will handle the request for a certain game
- a max Room ID will be maintained to ensure that every time a player enter the Room Select Phase, he/she would be able to join a new game
POST /join/{roomid}
Parameter name | type | comments |
---|---|---|
username | String | username, unique for one player |
roomid | int | ID of the room to join |
{
"username": "tenkitenki",
"roomid" : 8787
}
Same as Action Phase, please check that below.
POST /place/{roomid}
Parameter name | Type | Comments |
---|---|---|
PlayerId | int | Player's identity |
Placement | int[] | Placement[i] denotes the number of units the plaer deployed on territory with id i. The territory not belongs to the player will be set to -1. |
{
"PlayerId": 0,
"Placement": [
-1,
20,
30,
50,
-1,
-1
]
}
Same as Action Phase, please check that below.
- Add upgrade and switch game operation based on EVO1
- The game will go back to Room Select Phase when a switch game command is received from the player or the current game is over
POST /act/
Parameter name | type | comments |
---|---|---|
Player ID | int | Player's Identity |
MoveFrom | []int | src territories ids for move action |
MoveTo | [][]int | des territories ids for move action |
MoveTroop | []Unit | Units to be moved |
AttackFrom | []int | src territories ids for attack action |
AttackTo | []int | des territories ids for attack action |
AttackTroop | [][]Unit | Units to attack |
UserName | String | name of the user |
RoomId | int | room id |
UpgradeUnitTerritory | []int | The territories id where the unit to be upgraded locate |
UpgradeUnitId | []int | The unit id you wan upgraded |
UpgradeUnitLevel | []int | The number of level you want the unit upgrade. |
UpgradeTechnology | bool | True if player want upgrade technology level in this round |
{
"UserName": "player1",
"RoomId": 0,
"PlayerId": 1,
"MoveFrom" : [
0,
0,
1
],
"MoveTo" : [
1,
2,
3
],
"MoveTroop": [[
{
Name : "Guowang",
UnitId : 0,
LevelName : "primary school",
Level : 0,
CombatPts: 0
},
{
Name: "Yiheng",
UnitId : 1,
levelName : "PhD",
Level : 5
CombatPts: 11
}
]],
"AttackFrom" : [
0,
0,
1
],
"AttackTo" : [
1,
2,
3
],
"AttackTroop": [[
{
Name : "Quanzhi",
UnitId : 2,
LevelName : "master",
Level : 4,
CombatPts: 5
}
]],
"UpgradeUnitTerritory":[
0,
1,
1
],
"UpgradeUnitId":[
0,
3,
2,
],
"UpgradeUnitLevel":[
2,
2,
1
],
"UpgradeTechnology": True
}
same as POST /act/commit/
POST /act/attack/
Parameter name | type | comments |
---|---|---|
Player ID | int | Player's Identity |
From | int | src territoriy id for attack action |
To | int | des territory id for attack action |
Troop | []Unit | Units to attack |
UserName | String | name of the user |
RoomId | int | room id |
{
"UserName" : "Player1",
"RoomId" : 1,
"PlayerId" : 0,
"From" : 0,
"To" : 1,
"Troop" : [
{
Name : "Guowang",
UnitId : 0,
LevelName : "primary school",
Level : 0,
CombatPts: 0
},
{
Name: "Yiheng",
UnitId : 1,
levelName : "PhD",
Level : 5
CombatPts: 11
}
]
}
Parameter name | type | comments |
---|---|---|
Success | bool | Indicate if the operation succed |
ErrorCode | int | Error code to classify the error |
Information | int | Information related |
{
"Success" : False,
"ErrorCode" : 5,
"Information" : "Insufficient Resources",
}
POST /act/move/
Same as Attack
Same as Attack
POST /act/upgrade_unit/
Parameter name | type | comments |
---|---|---|
Player ID | int | Player's Identity |
TerritoryId | int | the territory where the unit locate |
UnitId | int | the unit id |
LevelUpgraded | int | Level to increase (lv2 -> lv5, should input 3) |
UserName | String | name of the user |
RoomId | int | room id |
{
"UserName" : "Player1",
"RoomId" : 1,
"PlayerId" : 0,
"TerritoryId": 0,
"UnitId" : 0,
"LevelUpgraded": 3
}
Parameter name | type | comments |
---|---|---|
Success | bool | Indicate if the operation succed |
ErrorCode | int | Error code to classify the error |
Information | int | Information related |
{
"Success" : False,
"ErrorCode" : 5,
"Information" : "Insufficient Resources",
}
POST /act/upgrade_tech/
Parameter name | type | comments |
---|---|---|
Player ID | int | Player's Identity |
UserName | String | name of the user |
RoomId | int | room id |
{
"UserName" : "Player1",
"RoomId" : 1,
"PlayerId" : 0,
}
Parameter name | type | comments |
---|---|---|
Success | bool | Indicate if the operation succed |
ErrorCode | int | Error code to classify the error |
Information | int | Information related |
{
"Success" : False,
"ErrorCode" : 5,
"Information" : "Insufficient Resources",
}
POST /act/commit/
Parameter name | type | comments |
---|---|---|
username | String | username, unique for one player |
roomids | []int | roomid available to join for the player |
{
"Username": "qf37",
"roomId": 1,
}
Parameter name | type | comments |
---|---|---|
PlayerInfo | Player | Information of players |
Territories | []int | Information of the map |
lose | bool | indicate if the play has lost |
end | bool | indicate if the game was over |
playerList | []String | name list of players in the room, with index identifying playerId |
{
"PlayerInfo" : {"PlayerId": 1,
"Resources": {
"Tech_pts" : 30,
"Tood_pts" : 30,
},
"TechnologyLevel": 2
},
"Territories" : [
{
"Name": "A",
"TerritoryId" : 0,
"Owner" : 1,
"Troop" : [
{
Name : "Guowang",
UnitId : 0,
LevelName : "primary school",
Level : 0,
CombatPts: 0
},
{
name: "Yiheng",
UnitId : 1,
levelName : "PhD",
Level : 5
CombatPts: 11
}
],
"Cost" : 30,
"Distance" : [0, 1, 2]
},
{
"Name" : "B",
"TerritoryId" : 1,
"Owner" : 2,
"Troop" : [
{
Name : "Quanzhi",
LevelName : "master",
Level : 4,
CombatPts: 5
}
],
"Cost" : 30,
"Distance" : [1, 0, 1]
},
{
"Name" : "C",
"TerritoryId" : 2,
"Owner" : 0,
"Troop" : [
{
Name : "Tenki",
UnitId : 2,
LevelName : "middle school",
Level : 1,
CombatPts: 3
},
{
Name: "Zoe",
UnitId : 3,
LevelName : "professor",
Level : 6
CombatPts: 15
}
],
"Cost" : 30,
"Distance" : [2, 1, 0]
}
],
"lose": false,
"end": false,
"playerList": ["Tenki", "Quanzhi", "Yiheng", "Zoe", "Guowang"]
}
Territory
Parameter name | Type | Comments |
---|---|---|
Name | string | Name of the territory |
TerritoryId | int | Unique Identity of the territory |
Owner | int | The player id which this territory belongs to |
Troop | []Unit | Units |
Distance | []int | Distance to other territories. Distance[i] indicate the distance toward territory with id i. |
Cost | int | number of food need to go through the territory |
Unit
Parameter name | Type | Comments |
---|---|---|
Name | string | unit name |
LevelName | int | name of this level |
Level | int | level of unit |
CombatPts | int | bonus of this level |
UnitId | int | id of unit |
Player
Parameter name | Type | Comments |
---|---|---|
PlayerId | int | PlayerId |
Resources | Resource | Resource the unit have |
TechnologyLevel | int | technology level |
Resource
Parameter name | Type | Comments |
---|---|---|
Tech_pts | int | Technology points |
Food_pts | int | Food points |
The UML class diagram of our design is shown below:
Control the game logic at the client side
Manage the initial connection with server as well as the later game process including sending request and receiving response.
Notice:
A player loses when he no longer controls any territories.
A player who has lost may continue to watch the game if he desires, or may disconnect.
Helps to present the current world. (text-based for Evo1)
Responsible for starting the server.
Responsible for receiving and parsing requests from the client, and pass it to handler for further process.
Pass the parsed request to the controller for further process, according to the different type of the command received.
Main body for processing the game logic!
Generate the initial state + Update the state of each turn + Check game over
Notice:
At the end of each turn, one new basic unit shall appear in each territory.
Move orders effectively occur before attack orders.
Orders may not create new units nor allow a unit to be in two places at once (attacking two territories).
When a player has won, the server should announce this to all remaining clients, which should display this information. The game then ends.
When a player has lost, the server should automatically consider his moves to be committed (as the empty set) at the start of each turn.
a. Player
b. AbstractWorldFactory (Abstract Factory)
Generate the world (the initial territories and soldier distribution for each player) according to the number of players(use hardcode).
The territories must form a connected graph (all territories must be reachable from any other territory).
Each territory shall be adjacent to one or more other territories.
Each player shall have the same number of initial units.
(1) Message
(2) Territory
Each territory shall be “owned” by one player at any given time.
a. CombatResolver
Responsible for the combat logic. Each territory should have an instance of this resolver. And every turn, the resolver of all territories should be traversed.
Combat Logic:
(a) Combat between one attacker and one defender is an iterative process which ends when one side runs out of units in the fight:
i. The server rolls two 20-sided dice (one for the attacker, one for the defender).
ii. The side with the lower roll loses 1 unit (in a tie, the defender wins).
(b) If player A attacks territory X with units from multiple of her own territories, they count as a single combined force.
(c) If multiple players attack the same territories, each attack is resolved sequentially, with the winner of the first attack being the defender in subsequent attacks. For example, if A,B, and C attack territory X held by player D, then B fights D first. If D wins, then C fights D. If C wins, then A fights C. The sequence in which the attacker’s actions are resolved should be randomly determined by the server.
(d) If units from territory X attack territory Y, and at the same time, units from territory Y attack territory X, then they are assumed to take drastically different routes between their territories, missing each other, and ending up at their destination with no combat in the middle. For example, if all units from X attack Y, and all units from Y attack X, then (assuming no other players attack those territories) both attacks will be successful with no units lost by either side (since there will be no defenders at the start of the battle).
b. Checker
i. Check if adjacency of the attack command
ii. Check if the move action is feasible