Compare commits
10 Commits
1e81e96ad4
...
9ffc92f1a6
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ffc92f1a6 | |||
| bfde39a595 | |||
| 11c13364b4 | |||
| 7f0800cf52 | |||
| 69cb4417fd | |||
| 37d9f84787 | |||
| 4874f110c1 | |||
| 6da608f90b | |||
| 9b1448edd8 | |||
| 52f7866707 |
17
.idea/libraries/junit_jupiter.xml
generated
Normal file
17
.idea/libraries/junit_jupiter.xml
generated
Normal file
@ -0,0 +1,17 @@
|
||||
<component name="libraryTable">
|
||||
<library name="junit.jupiter" type="repository">
|
||||
<properties maven-id="org.junit.jupiter:junit-jupiter:5.9.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-jupiter-5.9.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-jupiter-api-5.9.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-platform-commons-1.9.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/apiguardian-api-1.1.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-jupiter-params-5.9.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-jupiter-engine-5.9.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-platform-engine-1.9.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
13
.idea/libraries/mockito_core.xml
generated
Normal file
13
.idea/libraries/mockito_core.xml
generated
Normal file
@ -0,0 +1,13 @@
|
||||
<component name="libraryTable">
|
||||
<library name="mockito.core" type="repository">
|
||||
<properties maven-id="org.mockito:mockito-core:5.8.0" />
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/mockito-core-5.8.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/byte-buddy-1.14.10.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/byte-buddy-agent-1.14.10.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/objenesis-3.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
1
.idea/sqldialects.xml
generated
1
.idea/sqldialects.xml
generated
@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/720ef4bc-36c4-4861-8776-49b639600223/console.sql" dialect="PostgreSQL" />
|
||||
<file url="file://$PROJECT_DIR$/setup.sql" dialect="PostgreSQL" />
|
||||
</component>
|
||||
</project>
|
||||
@ -47,17 +47,17 @@ read -p "Press any key to resume ..." null
|
||||
|
||||
# --------------------------------------------------
|
||||
echo "3) create packages (done by admin)"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"845f0dc7-37d0-426e-994e-43fc3ac83c08\", \"Name\":\"WaterGoblin\", \"Damage\": 10.0}, {\"Id\":\"99f8f8dc-e25e-4a95-aa2c-782823f36e2a\", \"Name\":\"Dragon\", \"Damage\": 50.0}, {\"Id\":\"e85e3976-7c86-4d06-9a80-641c2019a79f\", \"Name\":\"WaterSpell\", \"Damage\": 20.0}, {\"Id\":\"1cb6ab86-bdb2-47e5-b6e4-68c5ab389334\", \"Name\":\"Ork\", \"Damage\": 45.0}, {\"Id\":\"dfdd758f-649c-40f9-ba3a-8657f4b3439f\", \"Name\":\"FireSpell\", \"Damage\": 25.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"845f0dc7-37d0-426e-994e-43fc3ac83c08\", \"Name\":\"WaterGoblin\", \"Damage\": 10.0, \"Crit\":30}, {\"Id\":\"99f8f8dc-e25e-4a95-aa2c-782823f36e2a\", \"Name\":\"Dragon\", \"Damage\": 50.0, \"Crit\":10}, {\"Id\":\"e85e3976-7c86-4d06-9a80-641c2019a79f\", \"Name\":\"WaterSpell\", \"Damage\": 20.0, \"Crit\":null}, {\"Id\":\"1cb6ab86-bdb2-47e5-b6e4-68c5ab389334\", \"Name\":\"Ork\", \"Damage\": 45.0, \"Crit\":0}, {\"Id\":\"dfdd758f-649c-40f9-ba3a-8657f4b3439f\", \"Name\":\"FireSpell\", \"Damage\": 25.0, \"Crit\":null}]"
|
||||
echo .
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"644808c2-f87a-4600-b313-122b02322fd5\", \"Name\":\"WaterGoblin\", \"Damage\": 9.0}, {\"Id\":\"4a2757d6-b1c3-47ac-b9a3-91deab093531\", \"Name\":\"Dragon\", \"Damage\": 55.0}, {\"Id\":\"91a6471b-1426-43f6-ad65-6fc473e16f9f\", \"Name\":\"WaterSpell\", \"Damage\": 21.0}, {\"Id\":\"4ec8b269-0dfa-4f97-809a-2c63fe2a0025\", \"Name\":\"Ork\", \"Damage\": 55.0}, {\"Id\":\"f8043c23-1534-4487-b66b-238e0c3c39b5\", \"Name\":\"WaterSpell\", \"Damage\": 23.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"644808c2-f87a-4600-b313-122b02322fd5\", \"Name\":\"WaterGoblin\", \"Damage\": 9.0, \"Crit\":15}, {\"Id\":\"4a2757d6-b1c3-47ac-b9a3-91deab093531\", \"Name\":\"Dragon\", \"Damage\": 55.0, \"Crit\":15}, {\"Id\":\"91a6471b-1426-43f6-ad65-6fc473e16f9f\", \"Name\":\"WaterSpell\", \"Damage\": 21.0, \"Crit\":null}, {\"Id\":\"4ec8b269-0dfa-4f97-809a-2c63fe2a0025\", \"Name\":\"Ork\", \"Damage\": 55.0, \"Crit\":5}, {\"Id\":\"f8043c23-1534-4487-b66b-238e0c3c39b5\", \"Name\":\"WaterSpell\", \"Damage\": 23.0, \"Crit\":null}]"
|
||||
echo .
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"b017ee50-1c14-44e2-bfd6-2c0c5653a37c\", \"Name\":\"WaterGoblin\", \"Damage\": 11.0}, {\"Id\":\"d04b736a-e874-4137-b191-638e0ff3b4e7\", \"Name\":\"Dragon\", \"Damage\": 70.0}, {\"Id\":\"88221cfe-1f84-41b9-8152-8e36c6a354de\", \"Name\":\"WaterSpell\", \"Damage\": 22.0}, {\"Id\":\"1d3f175b-c067-4359-989d-96562bfa382c\", \"Name\":\"Ork\", \"Damage\": 40.0}, {\"Id\":\"171f6076-4eb5-4a7d-b3f2-2d650cc3d237\", \"Name\":\"RegularSpell\", \"Damage\": 28.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"b017ee50-1c14-44e2-bfd6-2c0c5653a37c\", \"Name\":\"WaterGoblin\", \"Damage\": 11.0, \"Crit\":45}, {\"Id\":\"d04b736a-e874-4137-b191-638e0ff3b4e7\", \"Name\":\"Dragon\", \"Damage\": 70.0, \"Crit\":20}, {\"Id\":\"88221cfe-1f84-41b9-8152-8e36c6a354de\", \"Name\":\"WaterSpell\", \"Damage\": 22.0, \"Crit\":null}, {\"Id\":\"1d3f175b-c067-4359-989d-96562bfa382c\", \"Name\":\"Ork\", \"Damage\": 40.0, \"Crit\":0}, {\"Id\":\"171f6076-4eb5-4a7d-b3f2-2d650cc3d237\", \"Name\":\"RegularSpell\", \"Damage\": 28.0, \"Crit\":null}]"
|
||||
echo .
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"ed1dc1bc-f0aa-4a0c-8d43-1402189b33c8\", \"Name\":\"WaterGoblin\", \"Damage\": 10.0}, {\"Id\":\"65ff5f23-1e70-4b79-b3bd-f6eb679dd3b5\", \"Name\":\"Dragon\", \"Damage\": 50.0}, {\"Id\":\"55ef46c4-016c-4168-bc43-6b9b1e86414f\", \"Name\":\"WaterSpell\", \"Damage\": 20.0}, {\"Id\":\"f3fad0f2-a1af-45df-b80d-2e48825773d9\", \"Name\":\"Ork\", \"Damage\": 45.0}, {\"Id\":\"8c20639d-6400-4534-bd0f-ae563f11f57a\", \"Name\":\"WaterSpell\", \"Damage\": 25.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"ed1dc1bc-f0aa-4a0c-8d43-1402189b33c8\", \"Name\":\"WaterGoblin\", \"Damage\": 10.0, \"Crit\":30}, {\"Id\":\"65ff5f23-1e70-4b79-b3bd-f6eb679dd3b5\", \"Name\":\"Dragon\", \"Damage\": 50.0, \"Crit\":20}, {\"Id\":\"55ef46c4-016c-4168-bc43-6b9b1e86414f\", \"Name\":\"WaterSpell\", \"Damage\": 20.0, \"Crit\":null}, {\"Id\":\"f3fad0f2-a1af-45df-b80d-2e48825773d9\", \"Name\":\"Ork\", \"Damage\": 45.0, \"Crit\":10}, {\"Id\":\"8c20639d-6400-4534-bd0f-ae563f11f57a\", \"Name\":\"WaterSpell\", \"Damage\": 25.0, \"Crit\":null}]"
|
||||
echo .
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"d7d0cb94-2cbf-4f97-8ccf-9933dc5354b8\", \"Name\":\"WaterGoblin\", \"Damage\": 9.0}, {\"Id\":\"44c82fbc-ef6d-44ab-8c7a-9fb19a0e7c6e\", \"Name\":\"Dragon\", \"Damage\": 55.0}, {\"Id\":\"2c98cd06-518b-464c-b911-8d787216cddd\", \"Name\":\"WaterSpell\", \"Damage\": 21.0}, {\"Id\":\"951e886a-0fbf-425d-8df5-af2ee4830d85\", \"Name\":\"Ork\", \"Damage\": 55.0}, {\"Id\":\"dcd93250-25a7-4dca-85da-cad2789f7198\", \"Name\":\"FireSpell\", \"Damage\": 23.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"d7d0cb94-2cbf-4f97-8ccf-9933dc5354b8\", \"Name\":\"WaterGoblin\", \"Damage\": 9.0, \"Crit\":30}, {\"Id\":\"44c82fbc-ef6d-44ab-8c7a-9fb19a0e7c6e\", \"Name\":\"Dragon\", \"Damage\": 55.0, \"Crit\":10}, {\"Id\":\"2c98cd06-518b-464c-b911-8d787216cddd\", \"Name\":\"WaterSpell\", \"Damage\": 21.0, \"Crit\":null}, {\"Id\":\"951e886a-0fbf-425d-8df5-af2ee4830d85\", \"Name\":\"Ork\", \"Damage\": 55.0, \"Crit\":15}, {\"Id\":\"dcd93250-25a7-4dca-85da-cad2789f7198\", \"Name\":\"FireSpell\", \"Damage\": 23.0, \"Crit\":null}]"
|
||||
echo .
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"b2237eca-0271-43bd-87f6-b22f70d42ca4\", \"Name\":\"WaterGoblin\", \"Damage\": 11.0}, {\"Id\":\"9e8238a4-8a7a-487f-9f7d-a8c97899eb48\", \"Name\":\"Dragon\", \"Damage\": 70.0}, {\"Id\":\"d60e23cf-2238-4d49-844f-c7589ee5342e\", \"Name\":\"WaterSpell\", \"Damage\": 22.0}, {\"Id\":\"fc305a7a-36f7-4d30-ad27-462ca0445649\", \"Name\":\"Ork\", \"Damage\": 40.0}, {\"Id\":\"84d276ee-21ec-4171-a509-c1b88162831c\", \"Name\":\"RegularSpell\", \"Damage\": 28.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"b2237eca-0271-43bd-87f6-b22f70d42ca4\", \"Name\":\"WaterGoblin\", \"Damage\": 11.0, \"Crit\":20}, {\"Id\":\"9e8238a4-8a7a-487f-9f7d-a8c97899eb48\", \"Name\":\"Dragon\", \"Damage\": 70.0, \"Crit\":5}, {\"Id\":\"d60e23cf-2238-4d49-844f-c7589ee5342e\", \"Name\":\"WaterSpell\", \"Damage\": 22.0, \"Crit\":null}, {\"Id\":\"fc305a7a-36f7-4d30-ad27-462ca0445649\", \"Name\":\"Ork\", \"Damage\": 40.0, \"Crit\":10}, {\"Id\":\"84d276ee-21ec-4171-a509-c1b88162831c\", \"Name\":\"RegularSpell\", \"Damage\": 28.0, \"Crit\":null}]"
|
||||
echo .
|
||||
echo .
|
||||
|
||||
@ -95,11 +95,11 @@ read -p "Press any key to resume ..." null
|
||||
|
||||
# --------------------------------------------------
|
||||
echo "6) add new packages"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"67f9048f-99b8-4ae4-b866-d8008d00c53d\", \"Name\":\"WaterGoblin\", \"Damage\": 10.0}, {\"Id\":\"aa9999a0-734c-49c6-8f4a-651864b14e62\", \"Name\":\"RegularSpell\", \"Damage\": 50.0}, {\"Id\":\"d6e9c720-9b5a-40c7-a6b2-bc34752e3463\", \"Name\":\"Knight\", \"Damage\": 20.0}, {\"Id\":\"02a9c76e-b17d-427f-9240-2dd49b0d3bfd\", \"Name\":\"RegularSpell\", \"Damage\": 45.0}, {\"Id\":\"2508bf5c-20d7-43b4-8c77-bc677decadef\", \"Name\":\"FireElf\", \"Damage\": 25.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"67f9048f-99b8-4ae4-b866-d8008d00c53d\", \"Name\":\"WaterGoblin\", \"Damage\": 10.0, \"Crit\":50}, {\"Id\":\"aa9999a0-734c-49c6-8f4a-651864b14e62\", \"Name\":\"RegularSpell\", \"Damage\": 50.0, \"Crit\":null}, {\"Id\":\"d6e9c720-9b5a-40c7-a6b2-bc34752e3463\", \"Name\":\"Knight\", \"Damage\": 20.0}, {\"Id\":\"02a9c76e-b17d-427f-9240-2dd49b0d3bfd\", \"Name\":\"RegularSpell\", \"Damage\": 45.0, \"Crit\":null}, {\"Id\":\"2508bf5c-20d7-43b4-8c77-bc677decadef\", \"Name\":\"FireElf\", \"Damage\": 25.0, \"Crit\":70}]"
|
||||
echo .
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"70962948-2bf7-44a9-9ded-8c68eeac7793\", \"Name\":\"WaterGoblin\", \"Damage\": 9.0}, {\"Id\":\"74635fae-8ad3-4295-9139-320ab89c2844\", \"Name\":\"FireSpell\", \"Damage\": 55.0}, {\"Id\":\"ce6bcaee-47e1-4011-a49e-5a4d7d4245f3\", \"Name\":\"Knight\", \"Damage\": 21.0}, {\"Id\":\"a6fde738-c65a-4b10-b400-6fef0fdb28ba\", \"Name\":\"FireSpell\", \"Damage\": 55.0}, {\"Id\":\"a1618f1e-4f4c-4e09-9647-87e16f1edd2d\", \"Name\":\"FireElf\", \"Damage\": 23.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"70962948-2bf7-44a9-9ded-8c68eeac7793\", \"Name\":\"WaterGoblin\", \"Damage\": 9.0, \"Crit\":30}, {\"Id\":\"74635fae-8ad3-4295-9139-320ab89c2844\", \"Name\":\"FireSpell\", \"Damage\": 55.0, \"Crit\":null}, {\"Id\":\"ce6bcaee-47e1-4011-a49e-5a4d7d4245f3\", \"Name\":\"Knight\", \"Damage\": 21.0}, {\"Id\":\"a6fde738-c65a-4b10-b400-6fef0fdb28ba\", \"Name\":\"FireSpell\", \"Damage\": 55.0, \"Crit\":null}, {\"Id\":\"a1618f1e-4f4c-4e09-9647-87e16f1edd2d\", \"Name\":\"FireElf\", \"Damage\": 23.0, \"Crit\":60}]"
|
||||
echo .
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"2272ba48-6662-404d-a9a1-41a9bed316d9\", \"Name\":\"WaterGoblin\", \"Damage\": 11.0}, {\"Id\":\"3871d45b-b630-4a0d-8bc6-a5fc56b6a043\", \"Name\":\"Dragon\", \"Damage\": 70.0}, {\"Id\":\"166c1fd5-4dcb-41a8-91cb-f45dcd57cef3\", \"Name\":\"Knight\", \"Damage\": 22.0}, {\"Id\":\"237dbaef-49e3-4c23-b64b-abf5c087b276\", \"Name\":\"WaterSpell\", \"Damage\": 40.0}, {\"Id\":\"27051a20-8580-43ff-a473-e986b52f297a\", \"Name\":\"FireElf\", \"Damage\": 28.0}]"
|
||||
curl -i -X POST http://localhost:10001/packages --header "Content-Type: application/json" --header "Authorization: Bearer $token3" -d "[{\"Id\":\"2272ba48-6662-404d-a9a1-41a9bed316d9\", \"Name\":\"WaterGoblin\", \"Damage\": 11.0, \"Crit\":30}, {\"Id\":\"3871d45b-b630-4a0d-8bc6-a5fc56b6a043\", \"Name\":\"Dragon\", \"Damage\": 70.0, \"Crit\":20}, {\"Id\":\"166c1fd5-4dcb-41a8-91cb-f45dcd57cef3\", \"Name\":\"Knight\", \"Damage\": 22.0}, {\"Id\":\"237dbaef-49e3-4c23-b64b-abf5c087b276\", \"Name\":\"WaterSpell\", \"Damage\": 40.0, \"Crit\":null}, {\"Id\":\"27051a20-8580-43ff-a473-e986b52f297a\", \"Name\":\"FireElf\", \"Damage\": 28.0, \"Crit\":55}]"
|
||||
echo .
|
||||
echo .
|
||||
|
||||
|
||||
55
README.md
55
README.md
@ -1 +1,54 @@
|
||||
# mtcg
|
||||
# MTCG
|
||||
|
||||
This repository is for the 3rd semester project "MonsterTradingCardGame" for software-engineering 1.
|
||||
|
||||
## Setup
|
||||
|
||||
To run the project, modify the connection string in "connection_string.txt" to allow access to the database. The database structure can be recreated with the commands found in setup.sql
|
||||
|
||||
## Structure
|
||||
|
||||
The project consists of two main parts, the HTTP-server, and the actual application. Additionally, there are the database helper functions
|
||||
|
||||
### HTTP Server
|
||||
|
||||
The Server manages routing and resolving of requests, and sends them to the specified services.
|
||||
|
||||
### DB
|
||||
|
||||
The db part is an attempt to simplify database access, but due to some more complex calls this hasn't worked out completely. It still makes the code more readable, and abstracts SQL statements to enums.
|
||||
|
||||
### MTCG
|
||||
|
||||
The last part, the application itself, mostly consists of the Service classes, implementing the Service interface, and the corresponding main classes, which for the most part just interface with the database functions. There are two relevant exceptions: The battle class (and helpers), and the session handler:
|
||||
|
||||
Sessions are one of the few things, that isn't stored in the database, but in memory. Everytime a user makes a request that has to be authenticated, the session handler looks it up in its map, and compares the requirements (admin, username).
|
||||
|
||||
The battle handler is responsible for matching players, and creating a new battle. As multiple users might try to start a battle simultaneously, it needs to be thread-safe, and run the actual battles asynchronously. It implements a queue system, and creates a separate battle instance per two players. Since every new request also creates a new thread, the battle handler only needs to be thread-safe, and only one player runs the actual battle for it to be multithreading. Both players then read from their shared battle class once the battle is complete to retrieve the log.
|
||||
|
||||
## Potential improvements
|
||||
|
||||
- Currently, the exact REST path is discarded after the router resolves it, as multiple functions share the same service class. This could be alleviated by either creating a separate service per route, which however would result in about 20 services, or otherwise parsing the route, which may defeat the point of the service interface, depending on implementation. This inefficiency also results in another issue, the service implementations have quite cluttered switches.
|
||||
|
||||
- As previously mentioned, the db functions are not very complete, and might not improve db access, as some hacks and manually adapting the query is required in quite a few instances. For statements which are fully supported though, it simplifies db calls quite a bit. Depending on IDE the whole system might be obsolete though.
|
||||
|
||||
## Unit tests
|
||||
|
||||
There are two parts of the project which are tested through unit tests: the router and the fight logic. The router is relevant because it allows most other code to rely on different forms of testing, as the router is guaranteed to be working, the fight logic is relevant to test as other testing methods are not precise enough to map an error to a specific part of the code. For other methods integration tests are easier to integrate, and server the same purpose, assuming the database and router are working correctly (hence the unit tests for the router).
|
||||
|
||||
## Lessons learned
|
||||
|
||||
- Starting with the HTTP server made it quite easy testing the application with integration tests (manual using postman or automated using the provided curl script).
|
||||
- A separate db helper class would probably make sense for larger, more complex projects, but was overkill here.
|
||||
|
||||
## Unique feature
|
||||
|
||||
My unique feature is the ability to crit: Whenever two monsters fight (i.e. no spells involved), they can critically strike for an increase in damage of 50%. Each card has a crit modifier, default to 0.
|
||||
|
||||
## Further notes
|
||||
|
||||
The curl script was modified to automatically store the authentication tokens in variables.
|
||||
|
||||
Time tracking was added directly to the commits for a total of about 60 hours of work; commits without any time are less than 5 minutes.
|
||||
|
||||
https://github.com/Nanogamer7/swen-mtcg/
|
||||
1
connection_string.txt
Normal file
1
connection_string.txt
Normal file
@ -0,0 +1 @@
|
||||
jdbc:postgresql://localhost:5432/mydb?user=postgres&password=postgres
|
||||
BIN
lib/apiguardian-api-1.1.2.jar
Normal file
BIN
lib/apiguardian-api-1.1.2.jar
Normal file
Binary file not shown.
BIN
lib/byte-buddy-1.14.10.jar
Normal file
BIN
lib/byte-buddy-1.14.10.jar
Normal file
Binary file not shown.
BIN
lib/byte-buddy-agent-1.14.10.jar
Normal file
BIN
lib/byte-buddy-agent-1.14.10.jar
Normal file
Binary file not shown.
BIN
lib/hamcrest-core-1.1.jar
Normal file
BIN
lib/hamcrest-core-1.1.jar
Normal file
Binary file not shown.
BIN
lib/junit-4.10-extended-1.0.4.jar
Normal file
BIN
lib/junit-4.10-extended-1.0.4.jar
Normal file
Binary file not shown.
BIN
lib/junit-4.10.jar
Normal file
BIN
lib/junit-4.10.jar
Normal file
Binary file not shown.
BIN
lib/junit-jupiter-5.9.0.jar
Normal file
BIN
lib/junit-jupiter-5.9.0.jar
Normal file
Binary file not shown.
BIN
lib/junit-jupiter-api-5.9.0.jar
Normal file
BIN
lib/junit-jupiter-api-5.9.0.jar
Normal file
Binary file not shown.
BIN
lib/junit-jupiter-engine-5.9.0.jar
Normal file
BIN
lib/junit-jupiter-engine-5.9.0.jar
Normal file
Binary file not shown.
BIN
lib/junit-jupiter-params-5.9.0.jar
Normal file
BIN
lib/junit-jupiter-params-5.9.0.jar
Normal file
Binary file not shown.
BIN
lib/junit-platform-commons-1.9.0.jar
Normal file
BIN
lib/junit-platform-commons-1.9.0.jar
Normal file
Binary file not shown.
BIN
lib/junit-platform-engine-1.9.0.jar
Normal file
BIN
lib/junit-platform-engine-1.9.0.jar
Normal file
Binary file not shown.
BIN
lib/mockito-core-5.8.0.jar
Normal file
BIN
lib/mockito-core-5.8.0.jar
Normal file
Binary file not shown.
BIN
lib/objenesis-3.3.jar
Normal file
BIN
lib/objenesis-3.3.jar
Normal file
Binary file not shown.
BIN
lib/opentest4j-1.2.0.jar
Normal file
BIN
lib/opentest4j-1.2.0.jar
Normal file
Binary file not shown.
22
mtcg.iml
22
mtcg.iml
@ -1,23 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<component name="AdditionalModuleElements">
|
||||
<content url="file://$MODULE_DIR$" dumb="true">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module-library" scope="PROVIDED">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/projectlombok/lombok/1.18.30/lombok-1.18.30.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="library" name="fasterxml.jackson.core" level="project" />
|
||||
<orderEntry type="library" name="fasterxml.jackson.core.annotations" level="project" />
|
||||
<orderEntry type="library" name="fasterxml.jackson.core.databind" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
225
setup.sql
Normal file
225
setup.sql
Normal file
@ -0,0 +1,225 @@
|
||||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
-- Dumped from database version 16.0 (Debian 16.0-1.pgdg120+1)
|
||||
-- Dumped by pg_dump version 16.1
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- Name: cardtype; Type: TYPE; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE TYPE public.cardtype AS ENUM (
|
||||
'monster',
|
||||
'spell'
|
||||
);
|
||||
|
||||
|
||||
ALTER TYPE public.cardtype OWNER TO postgres;
|
||||
|
||||
--
|
||||
-- Name: delete_package(); Type: FUNCTION; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE FUNCTION public.delete_package() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
DELETE FROM packages
|
||||
WHERE uuid = OLD.package;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
||||
ALTER FUNCTION public.delete_package() OWNER TO postgres;
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_table_access_method = heap;
|
||||
|
||||
--
|
||||
-- Name: cards; Type: TABLE; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE TABLE public.cards (
|
||||
uuid uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
damage double precision DEFAULT 0 NOT NULL,
|
||||
crit integer DEFAULT 0,
|
||||
owner uuid,
|
||||
deck boolean DEFAULT false NOT NULL,
|
||||
trade boolean DEFAULT false NOT NULL,
|
||||
package uuid,
|
||||
name character varying,
|
||||
CONSTRAINT "trade or deck" CHECK (((deck IS FALSE) OR (trade IS FALSE)))
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.cards OWNER TO postgres;
|
||||
|
||||
--
|
||||
-- Name: packages; Type: TABLE; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE TABLE public.packages (
|
||||
uuid uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.packages OWNER TO postgres;
|
||||
|
||||
--
|
||||
-- Name: trades; Type: TABLE; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE TABLE public.trades (
|
||||
uuid uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
card uuid NOT NULL,
|
||||
card_type public.cardtype NOT NULL,
|
||||
min_dmg double precision NOT NULL,
|
||||
user_uuid uuid
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.trades OWNER TO postgres;
|
||||
|
||||
--
|
||||
-- Name: users; Type: TABLE; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE TABLE public.users (
|
||||
uuid uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
username character varying NOT NULL,
|
||||
password character varying NOT NULL,
|
||||
name character varying,
|
||||
bio character varying,
|
||||
image character varying,
|
||||
elo integer DEFAULT 100 NOT NULL,
|
||||
wins integer DEFAULT 0 NOT NULL,
|
||||
losses integer DEFAULT 0 NOT NULL,
|
||||
coins integer DEFAULT 20 NOT NULL,
|
||||
admin boolean DEFAULT false
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.users OWNER TO postgres;
|
||||
|
||||
--
|
||||
-- Data for Name: cards; Type: TABLE DATA; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: packages; Type: TABLE DATA; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: trades; Type: TABLE DATA; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Name: cards cards_pk; Type: CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.cards
|
||||
ADD CONSTRAINT cards_pk PRIMARY KEY (uuid);
|
||||
|
||||
|
||||
--
|
||||
-- Name: packages packages_pk; Type: CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.packages
|
||||
ADD CONSTRAINT packages_pk PRIMARY KEY (uuid);
|
||||
|
||||
|
||||
--
|
||||
-- Name: trades trades_pk; Type: CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.trades
|
||||
ADD CONSTRAINT trades_pk PRIMARY KEY (uuid);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users user_pk; Type: CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.users
|
||||
ADD CONSTRAINT user_pk PRIMARY KEY (uuid);
|
||||
|
||||
|
||||
--
|
||||
-- Name: users username; Type: CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.users
|
||||
ADD CONSTRAINT username UNIQUE (username);
|
||||
|
||||
|
||||
--
|
||||
-- Name: cards delete_package_trigger; Type: TRIGGER; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
CREATE TRIGGER delete_package_trigger AFTER UPDATE ON public.cards FOR EACH ROW WHEN (((new.package IS NULL) AND (old.package IS NOT NULL))) EXECUTE FUNCTION public.delete_package();
|
||||
|
||||
|
||||
--
|
||||
-- Name: cards cards_packages_uuid_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.cards
|
||||
ADD CONSTRAINT cards_packages_uuid_fk FOREIGN KEY (package) REFERENCES public.packages(uuid) ON DELETE SET NULL;
|
||||
|
||||
|
||||
--
|
||||
-- Name: cards cards_users_uuid_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.cards
|
||||
ADD CONSTRAINT cards_users_uuid_fk FOREIGN KEY (owner) REFERENCES public.users(uuid) ON DELETE SET NULL;
|
||||
|
||||
|
||||
--
|
||||
-- Name: trades trades_cards_uuid_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.trades
|
||||
ADD CONSTRAINT trades_cards_uuid_fk FOREIGN KEY (card) REFERENCES public.cards(uuid);
|
||||
|
||||
|
||||
--
|
||||
-- Name: trades trades_users_uuid_fk; Type: FK CONSTRAINT; Schema: public; Owner: postgres
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.trades
|
||||
ADD CONSTRAINT trades_users_uuid_fk FOREIGN KEY (user_uuid) REFERENCES public.users(uuid);
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
package at.nanopenguin.mtcg.application;
|
||||
|
||||
import at.nanopenguin.mtcg.Pair;
|
||||
import at.nanopenguin.mtcg.application.service.schemas.Card;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class Battle {
|
||||
private record RoundResult(String winnerName, Card winnerCard, String loserName, Card loserCard, boolean draw);
|
||||
private Pair<Combatant, Combatant> combatants;
|
||||
@Getter
|
||||
private volatile List<String> log = new ArrayList<>();
|
||||
@Getter @Setter
|
||||
private boolean firstPlayer = true;
|
||||
|
||||
public Battle(Combatant combatant) {
|
||||
this.combatants = new Pair<>(combatant, null);
|
||||
}
|
||||
|
||||
public void addCombatant(Combatant combatant) {
|
||||
this.combatants.setRight(combatant);
|
||||
}
|
||||
|
||||
public void start() throws SQLException {
|
||||
|
||||
do {
|
||||
this.playRound();
|
||||
} while (combatants.left().deckSize() > 0 && combatants.right().deckSize() > 0);
|
||||
|
||||
// placeholder
|
||||
boolean leftWins = new Random().nextBoolean();
|
||||
this.combatants.left().updateStats(leftWins);
|
||||
this.combatants.right().updateStats(!leftWins);
|
||||
}
|
||||
|
||||
private void playRound() {
|
||||
Pair<Card, Card> cards = new Pair<>(this.combatants.left().getCard(), this.combatants.right().getCard());
|
||||
RoundResult result = this.fight(cards.left(), cards.right());
|
||||
this.log.add()
|
||||
}
|
||||
|
||||
private boolean fight(Card left, Card right) {
|
||||
|
||||
}
|
||||
|
||||
private String createCombatString(RoundResult result) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package at.nanopenguin.mtcg;
|
||||
|
||||
import at.nanopenguin.mtcg.application.SessionHandler;
|
||||
import at.nanopenguin.mtcg.application.service.*;
|
||||
import at.nanopenguin.mtcg.http.HttpMethod;
|
||||
import at.nanopenguin.mtcg.http.Router;
|
||||
@ -10,33 +11,34 @@ import java.io.IOException;
|
||||
public class Main {
|
||||
public static void main(String[] args) throws IOException {
|
||||
Router router = new Router();
|
||||
SessionHandler sessionHandler = new SessionHandler();
|
||||
router.addRoute(HttpMethod.GET, "/test/{var}/service", new TestService(), new int[]{2});
|
||||
|
||||
/* users */
|
||||
router.addRoute(HttpMethod.POST, "/users", new UserService(), new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/users/{username}", new UserService(), new int[]{2});
|
||||
router.addRoute(HttpMethod.PUT, "/users/{username}", new UserService(), new int[]{2});
|
||||
router.addRoute(HttpMethod.POST, "/sessions", new UserService(), new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/users", new UserService(sessionHandler), new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/users/{username}", new UserService(sessionHandler), new int[]{2});
|
||||
router.addRoute(HttpMethod.PUT, "/users/{username}", new UserService(sessionHandler), new int[]{2});
|
||||
router.addRoute(HttpMethod.POST, "/sessions", new UserService(sessionHandler), new int[]{});
|
||||
|
||||
/* packages */
|
||||
router.addRoute(HttpMethod.POST, "/packages", new PackagesService(), new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/transactions/packages", new PackagesService(), new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/packages", new PackagesService(sessionHandler), new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/transactions/packages", new PackagesService(sessionHandler), new int[]{});
|
||||
|
||||
/* cards */
|
||||
router.addRoute(HttpMethod.GET, "/cards", new CardsService(), new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/deck", new CardsService(), new int[]{});
|
||||
router.addRoute(HttpMethod.PUT, "/deck", new CardsService(), new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/cards", new CardsService(sessionHandler), new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/deck", new CardsService(sessionHandler), new int[]{});
|
||||
router.addRoute(HttpMethod.PUT, "/deck", new CardsService(sessionHandler), new int[]{});
|
||||
|
||||
/* game */
|
||||
router.addRoute(HttpMethod.GET, "/stats", new GameService(), new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/scoreboard", new GameService(), new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/battles", new GameService(), new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/stats", new GameService(sessionHandler), new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/scoreboard", new GameService(sessionHandler), new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/battles", new GameService(sessionHandler), new int[]{});
|
||||
|
||||
/* trading */
|
||||
router.addRoute(HttpMethod.GET, "/tradings", new TradingService(), new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/tradings", new TradingService(), new int[]{});
|
||||
router.addRoute(HttpMethod.DELETE, "/tradings/{tradingDealId}", new TradingService(), new int[]{2});
|
||||
router.addRoute(HttpMethod.POST, "/tradings/{tradingDealId}", new TradingService(), new int[]{2});
|
||||
router.addRoute(HttpMethod.GET, "/tradings", new TradingService(sessionHandler), new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/tradings", new TradingService(sessionHandler), new int[]{});
|
||||
router.addRoute(HttpMethod.DELETE, "/tradings/{tradingDealId}", new TradingService(sessionHandler), new int[]{2});
|
||||
router.addRoute(HttpMethod.POST, "/tradings/{tradingDealId}", new TradingService(sessionHandler), new int[]{2});
|
||||
|
||||
Server server = new Server(10001, 10, router);
|
||||
server.start();
|
||||
@ -15,4 +15,13 @@ public class Pair<T, U> {
|
||||
|
||||
public T left() { return this.left; };
|
||||
public U right() { return this.right; };
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof Pair<?,?>) {
|
||||
return left.equals(((Pair<?, ?>) o).left) && right.equals(((Pair<?, ?>) o).right);
|
||||
}
|
||||
|
||||
return super.equals(o);
|
||||
}
|
||||
}
|
||||
193
src/main/java/at/nanopenguin/mtcg/application/Battle.java
Normal file
193
src/main/java/at/nanopenguin/mtcg/application/Battle.java
Normal file
@ -0,0 +1,193 @@
|
||||
package at.nanopenguin.mtcg.application;
|
||||
|
||||
import at.nanopenguin.mtcg.Pair;
|
||||
import at.nanopenguin.mtcg.application.service.schemas.Card;
|
||||
import lombok.*;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class Battle {
|
||||
|
||||
@RequiredArgsConstructor
|
||||
enum ElementMod {
|
||||
HALF(0.5),
|
||||
NONE(1),
|
||||
DOUBLE(2);
|
||||
|
||||
public final double percentMod;
|
||||
}
|
||||
record FightDTO(Combatant player, Card card) {};
|
||||
record RoundResult(FightDTO winner, FightDTO loser, boolean draw, ElementMod winnerMod, ElementMod loserMod) {};
|
||||
private Pair<Combatant, Combatant> combatants;
|
||||
@Getter
|
||||
private volatile List<String> log = new ArrayList<>();
|
||||
@Getter @Setter
|
||||
private boolean firstPlayer = true;
|
||||
|
||||
public Battle(Combatant combatant) {
|
||||
this.combatants = new Pair<>(combatant, null);
|
||||
}
|
||||
|
||||
public void addCombatant(Combatant combatant) {
|
||||
this.combatants.setRight(combatant);
|
||||
}
|
||||
|
||||
public void start() throws SQLException {
|
||||
boolean leftWins;
|
||||
|
||||
int round = 0;
|
||||
do {
|
||||
this.playRound(++round);
|
||||
if (round == 100) return; // don't update stats on draw
|
||||
|
||||
System.out.println(combatants.left().deckSize() + " - " + combatants.right().deckSize() );
|
||||
} while ((leftWins = combatants.left().deckSize() > 0) && combatants.right().deckSize() > 0);
|
||||
|
||||
this.combatants.left().updateStats(leftWins);
|
||||
this.combatants.right().updateStats(!leftWins);
|
||||
}
|
||||
|
||||
void playRound(int round) {
|
||||
RoundResult result = this.fight(
|
||||
new FightDTO(
|
||||
this.combatants.left(),
|
||||
this.combatants.left().getAndRemoveCard()),
|
||||
new FightDTO(
|
||||
this.combatants.right(),
|
||||
this.combatants.right().getAndRemoveCard()),
|
||||
true);
|
||||
|
||||
this.log.add(this.createCombatString(round, result));
|
||||
|
||||
result.winner().player().addCard( // return card to winner deck
|
||||
result.winner().card());
|
||||
|
||||
if (result.draw()) {
|
||||
result.loser().player().addCard( // on draw both players get their card
|
||||
result.loser().card());
|
||||
return;
|
||||
}
|
||||
|
||||
result.winner.player().addCard( // on lose winner gets losers card to deck
|
||||
result.loser().card());
|
||||
}
|
||||
|
||||
static boolean isImmune(Card defend, Card attack) {
|
||||
if (defend.name().equals("Dragon") && attack.name().endsWith("Goblin")) return true;
|
||||
if (defend.name().equals("Wizzard") && attack.name().equals("Ork")) return true;
|
||||
if (defend.name().equals("WaterSpell") && attack.name().equals("Knight")) return true;
|
||||
if (defend.name().equals("Kraken") && attack.name().endsWith("Spell")) return true;
|
||||
if (defend.name().equals("FireElf") && attack.name().equals("Dragon")) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum Element {
|
||||
NORMAL,
|
||||
FIRE,
|
||||
WATER;
|
||||
private Element[] strong;
|
||||
private Element[] weak;
|
||||
|
||||
// improvement: map to get mods straight from this enum
|
||||
|
||||
static {
|
||||
NORMAL.strong = new Element[]{WATER};
|
||||
NORMAL.weak = new Element[]{FIRE};
|
||||
|
||||
FIRE.strong = new Element[]{NORMAL};
|
||||
FIRE.weak = new Element[]{WATER};
|
||||
|
||||
WATER.strong = new Element[]{FIRE};
|
||||
WATER.weak = new Element[]{NORMAL};
|
||||
}
|
||||
}
|
||||
|
||||
static Pair<ElementMod, ElementMod> getElementMod(Card left, Card right) {
|
||||
Pair<ElementMod, ElementMod> returnMods = new Pair<>(ElementMod.NONE, ElementMod.NONE);
|
||||
|
||||
if (!left.name().endsWith("Spell") && !right.name().endsWith("Spell")) return returnMods;
|
||||
|
||||
Element leftElement = Element.NORMAL;
|
||||
Element rightElement = Element.NORMAL;
|
||||
|
||||
for (val value : Element.values()) {
|
||||
if (left.name().toLowerCase().startsWith(value.name().toLowerCase())) leftElement = value;
|
||||
if (right.name().toLowerCase().startsWith(value.name().toLowerCase())) rightElement = value;
|
||||
}
|
||||
|
||||
final Element finalLeftElement = leftElement; // lambdas
|
||||
final Element finalRightElement = rightElement;
|
||||
|
||||
if (Arrays.stream(leftElement.strong).anyMatch(value -> value == finalRightElement)) returnMods.setLeft(ElementMod.DOUBLE);
|
||||
else if (Arrays.stream(leftElement.weak).anyMatch(value -> value == finalRightElement)) returnMods.setLeft(ElementMod.HALF);
|
||||
|
||||
if (Arrays.stream(rightElement.strong).anyMatch(value -> value == finalLeftElement)) returnMods.setRight(ElementMod.DOUBLE);
|
||||
else if (Arrays.stream(rightElement.weak).anyMatch(value -> value == finalLeftElement)) returnMods.setRight(ElementMod.HALF);
|
||||
|
||||
return returnMods;
|
||||
}
|
||||
|
||||
static RoundResult fight(FightDTO left, FightDTO right, boolean allowCrit) {
|
||||
if (isImmune(left.card(), right.card())) return new RoundResult(left, right, false, ElementMod.NONE, ElementMod.NONE);
|
||||
if (isImmune(right.card(), left.card())) return new RoundResult(right, left, false, ElementMod.NONE, ElementMod.NONE);
|
||||
|
||||
Pair<ElementMod, ElementMod> dmgMods = getElementMod(left.card(), right.card());
|
||||
|
||||
allowCrit = allowCrit && left.card().crit() != null && right.card().crit() != null;
|
||||
|
||||
double leftCrit = allowCrit && new Random().nextInt() % 100 < left.card().crit() ? 1.5 : 1;
|
||||
double rightCrit = allowCrit && new Random().nextInt() % 100 < right.card().crit() ? 1.5 : 1;
|
||||
|
||||
boolean leftWins = left.card().damage() * dmgMods.left().percentMod * leftCrit >= right.card().damage() * dmgMods.right().percentMod * rightCrit;
|
||||
return new RoundResult(
|
||||
leftWins ? left : right,
|
||||
leftWins ? right : left,
|
||||
left.card().damage() * dmgMods.left().percentMod * leftCrit == right.card().damage() * dmgMods.right().percentMod * rightCrit,
|
||||
leftWins ? dmgMods.left() : dmgMods.right(),
|
||||
leftWins ? dmgMods.right() : dmgMods.left());
|
||||
}
|
||||
|
||||
static String createCombatString(int round, RoundResult result) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder
|
||||
.append(round)
|
||||
.append(": ")
|
||||
.append(String.format("%s: %s (%s damage)",
|
||||
result.winner().player().name,
|
||||
result.winner().card().name(),
|
||||
result.winner().card().damage()))
|
||||
.append(" vs. ")
|
||||
.append(String.format("%s: %s (%s damage)",
|
||||
result.loser().player().name,
|
||||
result.loser().card().name(),
|
||||
result.loser().card().damage()))
|
||||
.append(" => ");
|
||||
|
||||
if (result.winnerMod() != ElementMod.NONE || result.loserMod() != ElementMod.NONE) {
|
||||
stringBuilder
|
||||
.append(String.format("%s vs. %s",
|
||||
result.winner().card().damage(),
|
||||
result.loser().card().damage()))
|
||||
.append(" -> ")
|
||||
.append(String.format("%s vs. %s",
|
||||
result.winner().card().damage() * result.winnerMod().percentMod,
|
||||
result.loser().card().damage() * result.loserMod().percentMod))
|
||||
.append(" => ");
|
||||
}
|
||||
|
||||
if (result.draw()) {
|
||||
return stringBuilder.append("Draw").toString();
|
||||
}
|
||||
|
||||
stringBuilder
|
||||
.append(result.winner().card().name())
|
||||
.append(" wins");
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
||||
@ -47,7 +47,7 @@ public class Combatant {
|
||||
.executeUpdate();
|
||||
};
|
||||
|
||||
public Card getCard() {
|
||||
public Card getAndRemoveCard() {
|
||||
return this.deck.popRandom();
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ public class Package {
|
||||
.table(Table.CARDS);
|
||||
if (card.id() != null) query.parameter("uuid", card.id());
|
||||
if (query.parameter("damage", card.damage())
|
||||
.parameter("crit", card.crit())
|
||||
.parameter("name", card.name())
|
||||
.parameter("package", packageUuid)
|
||||
.executeUpdate() != 1){
|
||||
@ -12,21 +12,8 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class SessionHandler {
|
||||
private static SessionHandler INSTANCE;
|
||||
private final Map<UUID, UserInfo> sessions = new HashMap<>();
|
||||
|
||||
private SessionHandler() {
|
||||
|
||||
}
|
||||
|
||||
public static SessionHandler getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new SessionHandler();
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public synchronized UUID login(UserCredentials userCredentials) throws SQLException { // avoid multiple logins of same user
|
||||
|
||||
val result = DbQuery.builder()
|
||||
@ -20,6 +20,7 @@ public class UserCards extends User {
|
||||
.column("uuid AS id")
|
||||
.column("name")
|
||||
.column("damage")
|
||||
.column("crit")
|
||||
.condition("owner", userUuid);
|
||||
if (deckOnly) dbQueryBuilder.condition("deck", true);
|
||||
ArrayList<Card> cards = new ArrayList<>();
|
||||
@ -19,14 +19,19 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CardsService implements Service {
|
||||
private final SessionHandler sessionHandler;
|
||||
|
||||
public CardsService(SessionHandler sessionHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response handleRequest(HttpRequest request) throws JsonProcessingException, SQLException, ArrayIndexOutOfBoundsException {
|
||||
|
||||
UUID authToken = SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization"));
|
||||
if (SessionHandler.getInstance().verifyUUID(authToken) != TokenValidity.VALID)
|
||||
if (this.sessionHandler.verifyUUID(authToken) != TokenValidity.VALID)
|
||||
return new Response(HttpStatus.UNAUTHORIZED);
|
||||
UUID userUuid = SessionHandler.getInstance().userUuidFromToken(authToken);
|
||||
UUID userUuid = this.sessionHandler.userUuidFromToken(authToken);
|
||||
|
||||
if (request.getPath().split("/")[1].equals("cards") && request.getMethod() == HttpMethod.GET) {
|
||||
val result = UserCards.get(userUuid, false);
|
||||
@ -12,14 +12,19 @@ import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GameService implements Service {
|
||||
private final SessionHandler sessionHandler;
|
||||
|
||||
public GameService(SessionHandler sessionHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response handleRequest(HttpRequest request) throws JsonProcessingException, SQLException {
|
||||
|
||||
UUID authToken = SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization"));
|
||||
if (SessionHandler.getInstance().verifyUUID(authToken) != TokenValidity.VALID)
|
||||
if (this.sessionHandler.verifyUUID(authToken) != TokenValidity.VALID)
|
||||
return new Response(HttpStatus.UNAUTHORIZED);
|
||||
UUID userUuid = SessionHandler.getInstance().userUuidFromToken(authToken);
|
||||
UUID userUuid = this.sessionHandler.userUuidFromToken(authToken);
|
||||
|
||||
if (request.getPath().split("/")[1].equals("stats") && request.getMethod() == HttpMethod.GET) {
|
||||
return new Response(HttpStatus.OK, new ObjectMapper().writeValueAsString(User.getStats(userUuid)));
|
||||
@ -20,13 +20,18 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PackagesService implements Service {
|
||||
private final SessionHandler sessionHandler;
|
||||
|
||||
public PackagesService(SessionHandler sessionHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response handleRequest(HttpRequest request) throws JsonProcessingException, SQLException, ArrayIndexOutOfBoundsException {
|
||||
UUID token = SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization"));
|
||||
|
||||
if (request.getPath().split("/")[1].equals("packages") && request.getMethod() == HttpMethod.POST) {
|
||||
return switch (SessionHandler.getInstance().verifyUUID(token, true)) {
|
||||
return switch (this.sessionHandler.verifyUUID(token, true)) {
|
||||
case MISSING, INVALID -> new Response(HttpStatus.UNAUTHORIZED);
|
||||
case FORBIDDEN -> new Response(HttpStatus.FORBIDDEN);
|
||||
case VALID -> new Response(
|
||||
@ -37,12 +42,12 @@ public class PackagesService implements Service {
|
||||
}
|
||||
|
||||
if (String.join("/", Arrays.copyOfRange(request.getPath().split("/"), 1, 3)).equals("transactions/packages") && request.getMethod() == HttpMethod.POST) {
|
||||
if (SessionHandler.getInstance().verifyUUID(token) != TokenValidity.VALID) return new Response(HttpStatus.UNAUTHORIZED);
|
||||
val result = Package.addToUser(SessionHandler.getInstance().userUuidFromToken(token));
|
||||
if (this.sessionHandler.verifyUUID(token) != TokenValidity.VALID) return new Response(HttpStatus.UNAUTHORIZED);
|
||||
val result = Package.addToUser(this.sessionHandler.userUuidFromToken(token));
|
||||
if (result.left() == PurchaseStatus.SUCCESS) {
|
||||
return new Response(HttpStatus.OK, "application/json", new ObjectMapper().writeValueAsString(result.right()));
|
||||
}
|
||||
return new Response(switch (Package.addToUser(SessionHandler.getInstance().userUuidFromToken(token)).left()) {
|
||||
return new Response(switch (Package.addToUser(this.sessionHandler.userUuidFromToken(token)).left()) {
|
||||
case NO_PACKAGE_AVAILABLE -> HttpStatus.NOT_FOUND;
|
||||
case NOT_ENOUGH_MONEY -> HttpStatus.FORBIDDEN;
|
||||
default -> HttpStatus.INTERNAL;
|
||||
@ -15,13 +15,19 @@ import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TradingService implements Service {
|
||||
private final SessionHandler sessionHandler;
|
||||
|
||||
public TradingService(SessionHandler sessionHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response handleRequest(HttpRequest request) throws JsonProcessingException, ArrayIndexOutOfBoundsException, SQLException {
|
||||
|
||||
UUID authToken = SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization"));
|
||||
if (SessionHandler.getInstance().verifyUUID(authToken) != TokenValidity.VALID)
|
||||
if (this.sessionHandler.verifyUUID(authToken) != TokenValidity.VALID)
|
||||
return new Response(HttpStatus.UNAUTHORIZED);
|
||||
UUID userUuid = SessionHandler.getInstance().userUuidFromToken(authToken);
|
||||
UUID userUuid = this.sessionHandler.userUuidFromToken(authToken);
|
||||
|
||||
if (request.getPath().split("/")[1].equals("tradings")) {
|
||||
return switch (request.getMethod()) {
|
||||
@ -18,12 +18,17 @@ import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class UserService implements Service {
|
||||
private final SessionHandler sessionHandler;
|
||||
|
||||
public UserService(SessionHandler sessionHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response handleRequest(HttpRequest request) throws JsonProcessingException, SQLException, ArrayIndexOutOfBoundsException {
|
||||
if (request.getPath().split("/")[1].equals("sessions") && request.getMethod() == HttpMethod.POST) {
|
||||
// login
|
||||
UUID uuid = SessionHandler.getInstance().login(new ObjectMapper().readValue(request.getBody(), UserCredentials.class));
|
||||
UUID uuid = this.sessionHandler.login(new ObjectMapper().readValue(request.getBody(), UserCredentials.class));
|
||||
return uuid != null ?
|
||||
new Response(HttpStatus.OK, "application/json", uuid.toString()) :
|
||||
new Response(HttpStatus.UNAUTHORIZED);
|
||||
@ -33,7 +38,7 @@ public class UserService implements Service {
|
||||
return switch (request.getMethod()) {
|
||||
case GET -> {
|
||||
String username = request.getPath().split("/")[2];
|
||||
if (SessionHandler.getInstance().verifyUUID(SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.VALID)
|
||||
if (this.sessionHandler.verifyUUID(SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.VALID)
|
||||
yield new Response(HttpStatus.UNAUTHORIZED);
|
||||
val userData = User.retrieve(username);
|
||||
yield userData != null ?
|
||||
@ -47,7 +52,7 @@ public class UserService implements Service {
|
||||
case PUT -> {
|
||||
String username = request.getPath().split("/")[2];
|
||||
UserData userData = new ObjectMapper().readValue(request.getBody(), UserData.class);
|
||||
if (SessionHandler.getInstance().verifyUUID(SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.VALID)
|
||||
if (this.sessionHandler.verifyUUID(SessionHandler.tokenFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.VALID)
|
||||
yield new Response(HttpStatus.UNAUTHORIZED);
|
||||
yield User.update(username, userData) ? new Response(HttpStatus.OK) : new Response(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
@ -10,5 +10,6 @@ import java.util.UUID;
|
||||
public record Card(
|
||||
UUID id,
|
||||
String name,
|
||||
Float damage) {
|
||||
Float damage,
|
||||
Integer crit) {
|
||||
}
|
||||
@ -6,6 +6,9 @@ import lombok.NonNull;
|
||||
import lombok.Singular;
|
||||
import lombok.val;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
@ -14,7 +17,15 @@ import java.util.stream.Stream;
|
||||
|
||||
@Builder
|
||||
public final class DbQuery {
|
||||
private static final String connectionString = "jdbc:postgresql://localhost:5432/mydb?user=postgres&password=postgres";
|
||||
private static final String connectionString;
|
||||
|
||||
static {
|
||||
try {
|
||||
connectionString = Files.readString(Paths.get("connection_string.txt"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private final SqlCommand command;
|
||||
@ -143,8 +154,6 @@ public final class DbQuery {
|
||||
}
|
||||
statementExecutor.setObject(i++, value);
|
||||
}
|
||||
|
||||
System.out.println(sql);
|
||||
return statementExecutor.executeUpdate();
|
||||
}
|
||||
}
|
||||
@ -171,8 +180,6 @@ public final class DbQuery {
|
||||
}
|
||||
statementExecutor.setObject(i++, value);
|
||||
}
|
||||
|
||||
System.out.println(sql);
|
||||
return statementExecutor.execute();
|
||||
}
|
||||
}
|
||||
@ -200,7 +207,6 @@ public final class DbQuery {
|
||||
statementExecutor.setObject(i++, value);
|
||||
}
|
||||
|
||||
System.out.println(sql);
|
||||
try (ResultSet resultSet = statementExecutor.executeQuery()) {
|
||||
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
@ -1,6 +1,7 @@
|
||||
package at.nanopenguin.mtcg.http;
|
||||
|
||||
import at.nanopenguin.mtcg.application.service.Service;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.IntStream;
|
||||
@ -8,11 +9,14 @@ import java.util.stream.IntStream;
|
||||
public class Router {
|
||||
private final Map<HttpMethod, Map<String, Route>> routeMap = new HashMap<>();
|
||||
|
||||
public void addRoute(final HttpMethod method, final String route, final Service service, final int[] pathVars) {
|
||||
Map<String, Route> map = this.routeMap.get(method);
|
||||
if (method != null && map == null) {
|
||||
this.routeMap.put(method, (map = new HashMap<>()));
|
||||
public Router() {
|
||||
for (HttpMethod httpMethod : HttpMethod.values()) {
|
||||
this.routeMap.put(httpMethod, new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
public void addRoute(@NonNull final HttpMethod method, final String route, final Service service, final int[] pathVars) {
|
||||
Map<String, Route> map = this.routeMap.get(method);
|
||||
|
||||
List<String> routeComponents = new ArrayList<>(Arrays.asList(route.split("/")));
|
||||
for ( Integer pathVarPos : pathVars) {
|
||||
117
src/test/java/at/nanopenguin/mtcg/application/BattleTest.java
Normal file
117
src/test/java/at/nanopenguin/mtcg/application/BattleTest.java
Normal file
@ -0,0 +1,117 @@
|
||||
package at.nanopenguin.mtcg.application;
|
||||
|
||||
import at.nanopenguin.mtcg.Pair;
|
||||
import at.nanopenguin.mtcg.application.service.schemas.Card;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class BattleTest {
|
||||
private static Card cardWithName(String name) {
|
||||
Card cardMock = Mockito.mock(Card.class);
|
||||
Mockito.when(cardMock.name()).thenReturn(name);
|
||||
return cardMock;
|
||||
}
|
||||
|
||||
private static Card cardWithNameAndDmg(String name, float dmg) {
|
||||
Card cardMock = Mockito.mock(Card.class);
|
||||
Mockito.when(cardMock.name()).thenReturn(name);
|
||||
Mockito.when(cardMock.damage()).thenReturn(dmg);
|
||||
return cardMock;
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"WaterGoblin, FireSpell, DOUBLE, HALF", // generic
|
||||
"FireGoblin, NormalSpell, DOUBLE, HALF",
|
||||
"NormalGoblin, WaterSpell, DOUBLE, HALF",
|
||||
"FireGoblin, WaterSpell, HALF, DOUBLE", // generic reverse
|
||||
"NormalGoblin, FireSpell, HALF, DOUBLE",
|
||||
"WaterGoblin, NormalSpell, HALF, DOUBLE",
|
||||
"WaterGoblin, FireTroll, NONE, NONE", // no spell
|
||||
"WaterSpell, FireSpell, DOUBLE, HALF", // two spells
|
||||
"FireGoblin, FireSpell, NONE, NONE", // same element
|
||||
"FireSpell, Dragon, DOUBLE, HALF" // non elemental
|
||||
})
|
||||
void elementMods(String name1, String name2, Battle.ElementMod expectedMod1, Battle.ElementMod expectedMod2){
|
||||
Card card1 = cardWithName(name1);
|
||||
Card card2 = cardWithName(name2);
|
||||
|
||||
Assertions.assertEquals(new Pair<>(expectedMod1, expectedMod2), Battle.getElementMod(card1, card2));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"Dragon, FireGoblin, true",
|
||||
"Dragon, WaterGoblin, true",
|
||||
"Dragon, NormalGoblin, true",
|
||||
"WaterGoblin, Dragon, false",
|
||||
"Wizzard, Ork, true",
|
||||
"Ork, Wizzard, false",
|
||||
"WaterSpell, Knight, true",
|
||||
"FireSpell, Knight, false",
|
||||
"Knight, WaterSpell, false",
|
||||
"Kraken, WaterSpell, true",
|
||||
"Kraken, FireSpell, true",
|
||||
"FireElf, Dragon, true",
|
||||
"NormalElf, Dragon, false"
|
||||
})
|
||||
void immunityTest(String name1, String name2, boolean expected) {
|
||||
Card card1 = cardWithName(name1);
|
||||
Card card2 = cardWithName(name2);
|
||||
|
||||
Assertions.assertEquals(expected, Battle.isImmune(card1, card2));
|
||||
}
|
||||
|
||||
private static Battle.FightDTO getFightDTO(String cardName, float dmg) {
|
||||
Battle.FightDTO fightDTOmock = Mockito.mock(Battle.FightDTO.class);
|
||||
Card cardMock = cardWithNameAndDmg(cardName, dmg);
|
||||
Mockito.when(fightDTOmock.card()).thenReturn(cardMock);
|
||||
return fightDTOmock;
|
||||
}
|
||||
|
||||
enum fightOutcome {
|
||||
LEFT_WINS,
|
||||
DRAW,
|
||||
RIGHT_WINS
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"WaterGoblin, 10, FireTroll, 15, RIGHT_WINS, NONE, NONE",
|
||||
"FireTroll, 15, WaterGoblin, 10, LEFT_WINS, NONE, NONE",
|
||||
"FireSpell, 10, WaterSpell, 20, RIGHT_WINS, DOUBLE, HALF",
|
||||
"FireSpell, 20, WaterSpell, 5, DRAW, HALF, DOUBLE",
|
||||
"FireSpell, 90, WaterSpell, 5, LEFT_WINS, HALF, DOUBLE",
|
||||
"FireSpell, 10, WaterGoblin, 10, RIGHT_WINS, DOUBLE, HALF",
|
||||
"WaterSpell, 10, WaterGoblin, 10, DRAW, NONE, NONE",
|
||||
"RegularSpell, 10, WaterGoblin, 10, LEFT_WINS, DOUBLE, HALF",
|
||||
"RegularSpell, 10, Knight, 15, RIGHT_WINS, NONE, NONE",
|
||||
})
|
||||
void fightTest(String card1, float dmg1, String card2, float dmg2, fightOutcome outcome, Battle.ElementMod expectedMod1, Battle.ElementMod expectedMod2) {
|
||||
Battle.FightDTO fightDTO1 = getFightDTO(card1, dmg1);
|
||||
Battle.FightDTO fightDTO2 = getFightDTO(card2, dmg2);
|
||||
|
||||
Battle.RoundResult result = Battle.fight(fightDTO1, fightDTO2, false);
|
||||
|
||||
switch (outcome) {
|
||||
case LEFT_WINS -> {
|
||||
Assertions.assertEquals(fightDTO1, result.winner());
|
||||
Assertions.assertFalse(result.draw());
|
||||
}
|
||||
case DRAW -> {
|
||||
Assertions.assertTrue(result.draw());
|
||||
}
|
||||
case RIGHT_WINS -> {
|
||||
Assertions.assertEquals(fightDTO2, result.winner());
|
||||
Assertions.assertFalse(result.draw());
|
||||
}
|
||||
}
|
||||
|
||||
Assertions.assertEquals(expectedMod1, result.winnerMod());
|
||||
Assertions.assertEquals(expectedMod2, result.loserMod());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
145
src/test/java/at/nanopenguin/mtcg/http/RouterTest.java
Normal file
145
src/test/java/at/nanopenguin/mtcg/http/RouterTest.java
Normal file
@ -0,0 +1,145 @@
|
||||
package at.nanopenguin.mtcg.http;
|
||||
|
||||
import at.nanopenguin.mtcg.application.service.Service;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RouterTest {
|
||||
private static Router router;
|
||||
private static Service mockedService;
|
||||
private static Service mockedServiceUnwanted;
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
router = new Router();
|
||||
mockedService = Mockito.mock(Service.class);
|
||||
mockedServiceUnwanted = Mockito.mock(Service.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void simpleRoute() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get", mockedService, new int[]{});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void wrongPath() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get", mockedService, new int[]{});
|
||||
|
||||
Assertions.assertNull(router.resolveRoute(HttpMethod.GET, "/different"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void wrongMethod() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get", mockedService, new int[]{});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
|
||||
Assertions.assertNull(router.resolveRoute(HttpMethod.POST, "/get"));
|
||||
Assertions.assertNull(router.resolveRoute(HttpMethod.PUT, "/get"));
|
||||
Assertions.assertNull(router.resolveRoute(HttpMethod.DELETE, "/get"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentHttpExists() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get", mockedService, new int[]{});
|
||||
router.addRoute(HttpMethod.POST, "/get", mockedServiceUnwanted, new int[]{});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
Mockito.verify(mockedServiceUnwanted, Mockito.times(0)).handleRequest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void longerPathExists() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get", mockedService, new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/get/long", mockedServiceUnwanted, new int[]{});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
Mockito.verify(mockedServiceUnwanted, Mockito.times(0)).handleRequest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shorterPathExists() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get/path", mockedService, new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/get", mockedServiceUnwanted, new int[]{});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get/path");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
Mockito.verify(mockedServiceUnwanted, Mockito.times(0)).handleRequest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pathWithVar() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get/{var}", mockedService, new int[]{2});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get/value");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void longerPathWithVarExists() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get", mockedService, new int[]{});
|
||||
router.addRoute(HttpMethod.GET, "/get/{var}", mockedServiceUnwanted, new int[]{2});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
Mockito.verify(mockedServiceUnwanted, Mockito.times(0)).handleRequest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pathBeforeVarIsValid() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get/{var}", mockedService, new int[]{2});
|
||||
router.addRoute(HttpMethod.GET, "/get", mockedServiceUnwanted, new int[]{});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get/value");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
Mockito.verify(mockedServiceUnwanted, Mockito.times(0)).handleRequest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pathContinuesAfterVar() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get/{var}/path", mockedService, new int[]{2});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get/value/path");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pathWithTwoVars() throws SQLException, JsonProcessingException {
|
||||
router.addRoute(HttpMethod.GET, "/get/{var}/path/{var}", mockedService, new int[]{2, 4});
|
||||
|
||||
Service result = router.resolveRoute(HttpMethod.GET, "/get/value/path/other_value");
|
||||
Assertions.assertNotNull(result);
|
||||
result.handleRequest(null);
|
||||
Mockito.verify(mockedService, Mockito.times(1)).handleRequest(null);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user