diff --git a/.gitignore b/.gitignore
index 36b741e6..97d3341a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,168 @@ tests/out
.idea
.DS_Store
.*
+# Created by https://www.toptal.com/developers/gitignore/api/intellij,maven,java
+# Edit at https://www.toptal.com/developers/gitignore?templates=intellij,maven,java
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+# https://plugins.jetbrains.com/plugin/7973-sonarlint
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator-enh.xml
+.idea/**/markdown-navigator/
+
+# Cache file creation bug
+# See https://youtrack.jetbrains.com/issue/JBR-2257
+.idea/$CACHE_FILE$
+
+# CodeStream plugin
+# https://plugins.jetbrains.com/plugin/12206-codestream
+.idea/codestream.xml
+
+# Azure Toolkit for IntelliJ plugin
+# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
+.idea/**/azureSettings.xml
+
+### Java ###
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+
+### Maven ###
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+# https://github.com/takari/maven-wrapper#usage-without-binary-jar
+.mvn/wrapper/maven-wrapper.jar
+
+# Eclipse m2e generated files
+# Eclipse Core
+.project
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+*/target/*
+# End of https://www.toptal.com/developers/gitignore/api/intellij,maven,java
+
!.gitignore
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 00000000..bd84550a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,16 @@
+
+
+ 4.0.0
+
+ org.machinecoding
+ machineCoding-parent
+ 1.0-SNAPSHOT
+ pom
+
+
+ snakeAndLadder
+ splitWise
+
+
diff --git a/snakeAndLadder/pom.xml b/snakeAndLadder/pom.xml
new file mode 100644
index 00000000..dd698886
--- /dev/null
+++ b/snakeAndLadder/pom.xml
@@ -0,0 +1,34 @@
+
+
+ 4.0.0
+
+
+ org.machinecoding
+ machineCoding-parent
+ 1.0-SNAPSHOT
+
+
+ snakeAndLadder
+
+
+ 23
+ 23
+ UTF-8
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ RELEASE
+ test
+
+
+ org.mockito
+ mockito-core
+ 4.0.0
+ test
+
+
+
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/Main.java b/snakeAndLadder/src/main/java/org/machinecoding/Main.java
new file mode 100644
index 00000000..cf68cb56
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/Main.java
@@ -0,0 +1,41 @@
+package org.machinecoding;
+
+import org.machinecoding.models.Board;
+import org.machinecoding.services.GameController;
+import org.machinecoding.models.Dice;
+import org.machinecoding.models.Player;
+import org.machinecoding.models.teleports.Ladder;
+import org.machinecoding.models.teleports.Snake;
+import org.machinecoding.models.teleports.TeleporterEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+public class Main {
+ public static void main(String[] args) {
+ Scanner scan = new Scanner(System.in);
+ int targetCell = 100;
+
+ int numSnakes = scan.nextInt();
+ List teleporterList = new ArrayList<>();
+ for (int i = 0; i < numSnakes; i++)
+ teleporterList.add(new Snake(scan.nextInt(), scan.nextInt()));
+
+ int numLadder = scan.nextInt();
+ for (int i = 0; i < numLadder; i++)
+ teleporterList.add(new Ladder(scan.nextInt(), scan.nextInt()));
+
+ int numPlayers = scan.nextInt();
+ List playerList = new ArrayList<>();
+ for (int i = 0; i < numPlayers; i++)
+ playerList.add(new Player(scan.next(), 0, targetCell));
+
+ List diceList = new ArrayList<>();
+ diceList.add(new Dice());
+
+ GameController gameController = new GameController(new Board(targetCell),
+ teleporterList, playerList, diceList);
+ gameController.start();
+ }
+}
\ No newline at end of file
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/models/Board.java b/snakeAndLadder/src/main/java/org/machinecoding/models/Board.java
new file mode 100644
index 00000000..64f66bf8
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/models/Board.java
@@ -0,0 +1,7 @@
+package org.machinecoding.models;
+
+public class Board {
+ private int cells;
+
+ public Board(int cells) { this.cells = cells; }
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/models/Dice.java b/snakeAndLadder/src/main/java/org/machinecoding/models/Dice.java
new file mode 100644
index 00000000..c8aba8dd
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/models/Dice.java
@@ -0,0 +1,7 @@
+package org.machinecoding.models;
+
+public class Dice {
+ public int roll() {
+ return (int) (Math.random() * 6) + 1;
+ }
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/models/Player.java b/snakeAndLadder/src/main/java/org/machinecoding/models/Player.java
new file mode 100644
index 00000000..b9f54f75
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/models/Player.java
@@ -0,0 +1,29 @@
+package org.machinecoding.models;
+
+public class Player {
+ private final String name;
+ private int position;
+ private final int targetPosition;
+
+ public Player(String name, int startPosition, int targetPosition){
+ this.name = name;
+ this.position = startPosition;
+ this.targetPosition = targetPosition;
+ }
+
+ public void move(int newPosition){
+ this.position = (newPosition > targetPosition) ? position : newPosition;
+ }
+
+ public boolean isWinner(){
+ return (position == targetPosition);
+ }
+
+ public int getPosition(){
+ return position;
+ }
+
+ public String getName(){
+ return name;
+ }
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Ladder.java b/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Ladder.java
new file mode 100644
index 00000000..896afcee
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Ladder.java
@@ -0,0 +1,7 @@
+package org.machinecoding.models.teleports;
+
+public class Ladder extends TeleporterEntity {
+ public Ladder(int startCell, int endCell) {
+ super(startCell, endCell);
+ }
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Snake.java b/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Snake.java
new file mode 100644
index 00000000..63f45446
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Snake.java
@@ -0,0 +1,7 @@
+package org.machinecoding.models.teleports;
+
+public class Snake extends TeleporterEntity {
+ public Snake(int startCell, int endCell) {
+ super(startCell, endCell);
+ }
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Teleporter.java b/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Teleporter.java
new file mode 100644
index 00000000..7ce3302f
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/Teleporter.java
@@ -0,0 +1,6 @@
+package org.machinecoding.models.teleports;
+
+public interface Teleporter {
+ boolean canBeUsed(int currentPosition);
+ int teleport();
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/TeleporterEntity.java b/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/TeleporterEntity.java
new file mode 100644
index 00000000..3e03eb8f
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/models/teleports/TeleporterEntity.java
@@ -0,0 +1,29 @@
+package org.machinecoding.models.teleports;
+
+public class TeleporterEntity implements Teleporter {
+ private final int startCell;
+ private final int endCell;
+
+ public TeleporterEntity(int startCell, int endCell){
+ this.startCell = startCell;
+ this.endCell = endCell;
+ }
+
+ @Override
+ public boolean canBeUsed(int currentPosition) {
+ return (startCell == currentPosition);
+ }
+
+ @Override
+ public int teleport() {
+ return endCell;
+ }
+
+ public int getStartCell() {
+ return startCell;
+ }
+
+ public int getEndCell() {
+ return endCell;
+ }
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/services/GameController.java b/snakeAndLadder/src/main/java/org/machinecoding/services/GameController.java
new file mode 100644
index 00000000..04bf1e22
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/services/GameController.java
@@ -0,0 +1,57 @@
+package org.machinecoding.services;
+
+import org.machinecoding.models.Board;
+import org.machinecoding.models.Dice;
+import org.machinecoding.models.Player;
+import org.machinecoding.models.teleports.TeleporterEntity;
+import org.machinecoding.strategy.DiceMovementStrategy;
+import org.machinecoding.strategy.GameMovementStrategy;
+import org.machinecoding.strategy.TeleporterMovementStrategy;
+
+import java.util.List;
+
+public class GameController {
+ private final Board board;
+ private final List teleports;
+ private final List players;
+ private final List dices;
+
+ private GameMovementStrategy movementStrategy;
+
+ public GameController(Board board, List teleports, List players, List dices) {
+ this.board = board;
+ this.teleports = teleports;
+ this.players = players;
+ this.dices = dices;
+
+ this.movementStrategy = new GameMovementStrategy(new DiceMovementStrategy(dices), new TeleporterMovementStrategy(teleports));
+ }
+
+ public void start() {
+ while(shouldPerformRound()){
+ performOneRound();
+ }
+ }
+
+ private boolean shouldPerformRound() {
+ return players.stream().filter(p -> !p.isWinner()).count() > 1;
+ }
+
+ public void performOneRound(){
+ for (Player player : players) {
+ if(!shouldPerformRound()) return;
+
+ int currentPosition = player.getPosition();
+ int newPosition = movementStrategy.move(currentPosition);
+ player.move(newPosition);
+
+ System.out.println(player.getName() + " rolled a " + movementStrategy.getDiceValue() + " and moved from " +
+ currentPosition + " to " + player.getPosition());
+
+ if (player.isWinner()) {
+ System.out.println(player.getName() + " wins the game");
+ }
+ }
+ }
+}
+
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/strategy/DiceMovementStrategy.java b/snakeAndLadder/src/main/java/org/machinecoding/strategy/DiceMovementStrategy.java
new file mode 100644
index 00000000..a01d73de
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/strategy/DiceMovementStrategy.java
@@ -0,0 +1,22 @@
+package org.machinecoding.strategy;
+
+import org.machinecoding.models.Dice;
+
+import java.util.List;
+
+public class DiceMovementStrategy implements MovementStrategy {
+ private final List dices;
+
+ public DiceMovementStrategy(List dices) {
+ this.dices = dices;
+ }
+
+ @Override
+ public int move(int pos) {
+ int steps = 0;
+ for (Dice dice : dices) {
+ steps += dice.roll();
+ }
+ return pos + steps;
+ }
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/strategy/GameMovementStrategy.java b/snakeAndLadder/src/main/java/org/machinecoding/strategy/GameMovementStrategy.java
new file mode 100644
index 00000000..64a6c1af
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/strategy/GameMovementStrategy.java
@@ -0,0 +1,34 @@
+package org.machinecoding.strategy;
+
+public class GameMovementStrategy implements MovementStrategy {
+ final DiceMovementStrategy diceMovementStrategy;
+ final TeleporterMovementStrategy teleporterMovementStrategy;
+
+ private int diceValue = 0;
+ private int teleporterValue = 0;
+
+ public GameMovementStrategy(DiceMovementStrategy diceMovementStrategy,
+ TeleporterMovementStrategy teleporterMovementStrategy) {
+ this.diceMovementStrategy = diceMovementStrategy;
+ this.teleporterMovementStrategy = teleporterMovementStrategy;
+ }
+
+ @Override
+ public int move(int pos) {
+ int diceMovedPosition = diceMovementStrategy.move(pos);
+ this.diceValue = diceMovedPosition - pos;
+
+ int teleporterMovedPosition = teleporterMovementStrategy.move(diceMovedPosition);
+ this.teleporterValue = teleporterMovedPosition - diceMovedPosition;
+
+ return teleporterMovedPosition;
+ }
+
+ public int getDiceValue() {
+ return diceValue;
+ }
+ public int getTeleporterValue() {
+ return teleporterValue;
+ }
+
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/strategy/MovementStrategy.java b/snakeAndLadder/src/main/java/org/machinecoding/strategy/MovementStrategy.java
new file mode 100644
index 00000000..f7931c34
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/strategy/MovementStrategy.java
@@ -0,0 +1,6 @@
+package org.machinecoding.strategy;
+
+public interface MovementStrategy {
+ int move(int pos);
+
+}
diff --git a/snakeAndLadder/src/main/java/org/machinecoding/strategy/TeleporterMovementStrategy.java b/snakeAndLadder/src/main/java/org/machinecoding/strategy/TeleporterMovementStrategy.java
new file mode 100644
index 00000000..58d537ea
--- /dev/null
+++ b/snakeAndLadder/src/main/java/org/machinecoding/strategy/TeleporterMovementStrategy.java
@@ -0,0 +1,37 @@
+package org.machinecoding.strategy;
+
+import org.machinecoding.models.teleports.TeleporterEntity;
+
+import java.util.List;
+
+public class TeleporterMovementStrategy implements MovementStrategy {
+ private final List teleports;
+
+ public TeleporterMovementStrategy(List teleports) {
+ this.teleports = teleports;
+ }
+
+ @Override
+ public int move(int pos) {
+ int currentPosition = pos;
+
+ while (true) {
+ boolean teleported = false;
+
+ for (TeleporterEntity teleport : teleports) {
+ if (teleport.canBeUsed(currentPosition)) {
+ currentPosition = teleport.teleport();
+ teleported = true;
+ break;
+ }
+ }
+
+ if (!teleported) {
+ break;
+ }
+ }
+
+ return currentPosition;
+ }
+
+}
diff --git a/snakeAndLadder/src/test/java/org/machinecoding/strategy/DiceMovementStrategyTest.java b/snakeAndLadder/src/test/java/org/machinecoding/strategy/DiceMovementStrategyTest.java
new file mode 100644
index 00000000..9e21c5b6
--- /dev/null
+++ b/snakeAndLadder/src/test/java/org/machinecoding/strategy/DiceMovementStrategyTest.java
@@ -0,0 +1,77 @@
+package org.machinecoding.strategy;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.machinecoding.models.Dice;
+import org.mockito.Mockito;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+class DiceMovementStrategyTest {
+
+ private DiceMovementStrategy diceMovementStrategy;
+ private List mockDices;
+
+ @BeforeEach
+ void setUp() {
+ mockDices = List.of(Mockito.mock(Dice.class), Mockito.mock(Dice.class));
+ diceMovementStrategy = new DiceMovementStrategy(mockDices);
+ }
+
+ // Test Scenario: Test the DiceMovementStrategy class to ensure it correctly implements the MovementStrategy interface,
+ // focusing on the move method's ability to calculate the new position based on dice rolls.
+ @ParameterizedTest
+ @CsvSource({
+ "0, 3, 4, 7",
+ "5, 2, 6, 13",
+ "10, 1, 1, 12"
+ })
+ void testMoveMethodCalculatesNewPosition(int initialPosition, int roll1, int roll2, int expectedPosition) {
+ when(mockDices.get(0).roll()).thenReturn(roll1);
+ when(mockDices.get(1).roll()).thenReturn(roll2);
+
+ int newPosition = diceMovementStrategy.move(initialPosition);
+
+ assertEquals(expectedPosition, newPosition);
+ }
+
+ // Test Scenario: Test the move method with an empty list of dice to ensure it returns the initial position without changes.
+ @Test
+ void testMoveMethodWithEmptyDiceList() {
+ DiceMovementStrategy strategyWithNoDice = new DiceMovementStrategy(List.of());
+ int initialPosition = 10;
+
+ int newPosition = strategyWithNoDice.move(initialPosition);
+
+ assertEquals(initialPosition, newPosition);
+ }
+
+ // Test Scenario: Test the move method with maximum dice roll values to ensure it calculates the correct new position.
+ @Test
+ void testMoveMethodWithMaximumDiceRolls() {
+ when(mockDices.get(0).roll()).thenReturn(6);
+ when(mockDices.get(1).roll()).thenReturn(6);
+ int initialPosition = 0;
+
+ int newPosition = diceMovementStrategy.move(initialPosition);
+
+ assertEquals(12, newPosition);
+ }
+
+ // Test Scenario: Test the move method with minimum dice roll values to ensure it calculates the correct new position.
+ @Test
+ void testMoveMethodWithMinimumDiceRolls() {
+ when(mockDices.get(0).roll()).thenReturn(1);
+ when(mockDices.get(1).roll()).thenReturn(1);
+ int initialPosition = 0;
+
+ int newPosition = diceMovementStrategy.move(initialPosition);
+
+ assertEquals(2, newPosition);
+ }
+}
\ No newline at end of file
diff --git a/snakeAndLadder/src/test/java/org/machinecoding/strategy/GameMovementStrategyTest.java b/snakeAndLadder/src/test/java/org/machinecoding/strategy/GameMovementStrategyTest.java
new file mode 100644
index 00000000..f9c0ad8c
--- /dev/null
+++ b/snakeAndLadder/src/test/java/org/machinecoding/strategy/GameMovementStrategyTest.java
@@ -0,0 +1,202 @@
+package org.machinecoding.strategy;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.when;
+
+class GameMovementStrategyTest {
+
+ private DiceMovementStrategy diceMovementStrategy;
+ private TeleporterMovementStrategy teleporterMovementStrategy;
+ private GameMovementStrategy gameMovementStrategy;
+
+ @BeforeEach
+ void setUp() {
+ diceMovementStrategy = Mockito.mock(DiceMovementStrategy.class);
+ teleporterMovementStrategy = Mockito.mock(TeleporterMovementStrategy.class);
+ gameMovementStrategy = new GameMovementStrategy(diceMovementStrategy, teleporterMovementStrategy);
+ }
+
+ // Test Scenario: Test the move method to ensure it correctly calculates the new position using both dice and teleporter strategies, and updates diceValue and teleporterValue accordingly.
+ @Test
+ void testMoveMethodCalculatesNewPositionAndUpdatesValues() {
+ int initialPosition = 0;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(5);
+ when(teleporterMovementStrategy.move(5)).thenReturn(10);
+
+ int finalPosition = gameMovementStrategy.move(initialPosition);
+
+ assertEquals(10, finalPosition);
+ assertEquals(5, gameMovementStrategy.getDiceValue());
+ assertEquals(5, gameMovementStrategy.getTeleporterValue());
+ }
+
+ // Test Scenario: Test if the GameMovementStrategy correctly utilizes the DiceMovementStrategy to calculate the diceValue when move is called.
+ @Test
+ void testDiceValueCalculation() {
+ int initialPosition = 2;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(7);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getDiceValue());
+ }
+
+ // Test Scenario: Test the behavior of GameMovementStrategy when teleporterMovementStrategy returns a position that is significantly different from the diceMovementStrategy's result, ensuring teleporterValue is calculated correctly.
+ @Test
+ void testTeleporterValueWithSignificantDifference() {
+ int initialPosition = 3;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(8);
+ when(teleporterMovementStrategy.move(8)).thenReturn(20);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(12, gameMovementStrategy.getTeleporterValue());
+ }
+
+ // Test Scenario: Verify that the diceValue is correctly updated after the move method is called, reflecting the difference between the initial position and the position after the dice movement strategy is applied.
+ @Test
+ void testDiceValueUpdateAfterMove() {
+ int initialPosition = 4;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(9);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getDiceValue());
+ }
+
+ // Test Scenario: Test teleporterValue calculation when teleporterMovementStrategy moves the position forward.
+ @Test
+ void testTeleporterValueCalculationForwardMove() {
+ int initialPosition = 5;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(10);
+ when(teleporterMovementStrategy.move(10)).thenReturn(15);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getTeleporterValue());
+ }
+
+ // Test Scenario: Test the constructor to ensure it correctly initializes the GameMovementStrategy with given DiceMovementStrategy and TeleporterMovementStrategy instances.
+ @Test
+ void testConstructorInitialization() {
+ GameMovementStrategy strategy = new GameMovementStrategy(diceMovementStrategy, teleporterMovementStrategy);
+ assertEquals(diceMovementStrategy, strategy.diceMovementStrategy);
+ assertEquals(teleporterMovementStrategy, strategy.teleporterMovementStrategy);
+ }
+
+ // Test Scenario: Verify that the GameMovementStrategy constructor correctly assigns the provided DiceMovementStrategy instance to the diceMovementStrategy field, ensuring that subsequent calls to move() use this instance for dice movement calculations.
+ @Test
+ void testDiceMovementStrategyAssignmentInConstructor() {
+ GameMovementStrategy strategy = new GameMovementStrategy(diceMovementStrategy, teleporterMovementStrategy);
+ assertEquals(diceMovementStrategy, strategy.diceMovementStrategy);
+ }
+
+ // Test Scenario: Verify that the teleporterMovementStrategy correctly updates the teleporterValue after moving from the diceMovedPosition, ensuring the teleporterValue reflects the difference between the teleporterMovedPosition and diceMovedPosition.
+ @Test
+ void testTeleporterMovementStrategyUpdatesTeleporterValue() {
+ int initialPosition = 6;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(11);
+ when(teleporterMovementStrategy.move(11)).thenReturn(16);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getTeleporterValue());
+ }
+
+ // Test Scenario: Test the move method with a starting position of 0, ensuring the dice and teleporter strategies are called correctly, and the final position is calculated accurately.
+ @Test
+ void testMoveMethodWithStartingPositionZero() {
+ int initialPosition = 0;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(3);
+ when(teleporterMovementStrategy.move(3)).thenReturn(8);
+
+ int finalPosition = gameMovementStrategy.move(initialPosition);
+
+ assertEquals(8, finalPosition);
+ }
+
+ // Test Scenario: Test the move method to ensure it correctly updates diceValue when diceMovementStrategy returns a positive move.
+ @Test
+ void testDiceValueUpdateWithPositiveMove() {
+ int initialPosition = 7;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(12);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getDiceValue());
+ }
+
+ // Test Scenario: Verify that the diceValue is correctly updated when the move method is called, ensuring it reflects the difference between the new position after dice movement and the initial position.
+ @Test
+ void testDiceValueCorrectUpdateAfterMove() {
+ int initialPosition = 8;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(13);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getDiceValue());
+ }
+
+ // Test Scenario: Test teleporter movement when dice movement results in a position that triggers a teleportation event.
+ @Test
+ void testTeleporterMovementOnDiceTrigger() {
+ int initialPosition = 9;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(14);
+ when(teleporterMovementStrategy.move(14)).thenReturn(19);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getTeleporterValue());
+ }
+
+ // Test Scenario: Test teleporterValue calculation when teleporterMovementStrategy moves forward by 5 positions from a diceMovedPosition of 10.
+ @Test
+ void testTeleporterValueCalculationWithForwardMove() {
+ int initialPosition = 10;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(15);
+ when(teleporterMovementStrategy.move(15)).thenReturn(20);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getTeleporterValue());
+ }
+
+ // Test Scenario: Verify that the move method correctly updates the teleporterMovedPosition based on the teleporterMovementStrategy's move result, ensuring the teleporterValue reflects the difference between the teleporterMovedPosition and diceMovedPosition.
+ @Test
+ void testTeleporterMovedPositionUpdate() {
+ int initialPosition = 11;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(16);
+ when(teleporterMovementStrategy.move(16)).thenReturn(21);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getTeleporterValue());
+ }
+
+ // Test Scenario: Verify that the getDiceValue() method returns the correct dice movement value after a move operation is performed.
+ @Test
+ void testGetDiceValueMethod() {
+ int initialPosition = 12;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(17);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getDiceValue());
+ }
+
+ // Test Scenario: Verify that getTeleporterValue() returns the correct teleporter movement value after move() is called.
+ @Test
+ void testGetTeleporterValueMethod() {
+ int initialPosition = 13;
+ when(diceMovementStrategy.move(initialPosition)).thenReturn(18);
+ when(teleporterMovementStrategy.move(18)).thenReturn(23);
+
+ gameMovementStrategy.move(initialPosition);
+
+ assertEquals(5, gameMovementStrategy.getTeleporterValue());
+ }
+}
\ No newline at end of file
diff --git a/splitWise/pom.xml b/splitWise/pom.xml
new file mode 100644
index 00000000..25abd6db
--- /dev/null
+++ b/splitWise/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+
+ org.machinecoding
+ machineCoding-parent
+ 1.0-SNAPSHOT
+
+
+ splitWise
+
+
+ 23
+ 23
+ UTF-8
+
+
+
\ No newline at end of file
diff --git a/splitWise/src/main/java/org/machinecoding/Constants.java b/splitWise/src/main/java/org/machinecoding/Constants.java
new file mode 100644
index 00000000..9aec1377
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/Constants.java
@@ -0,0 +1,10 @@
+package org.machinecoding;
+
+public class Constants {
+ public static final String SHOW = "SHOW";
+ public static final String EXPENSE = "EXPENSE";
+
+ public static final String EQUAL = "EQUAL";
+ public static final String EXACT = "EXACT";
+ public static final String PERCENTAGE = "PERCENT";
+}
diff --git a/splitWise/src/main/java/org/machinecoding/Main.java b/splitWise/src/main/java/org/machinecoding/Main.java
new file mode 100644
index 00000000..68326308
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/Main.java
@@ -0,0 +1,97 @@
+package org.machinecoding;
+
+import org.machinecoding.models.Expense;
+import org.machinecoding.models.User;
+import org.machinecoding.services.SplitWiseRunner;
+import org.machinecoding.strategy.EqualSplittingStrategy;
+import org.machinecoding.strategy.ExactSplittingStrategy;
+import org.machinecoding.strategy.PercentageSplittingStrategy;
+import org.machinecoding.strategy.SplittingStrategy;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+
+import static org.machinecoding.Constants.*;
+
+public class Main {
+ public static void main(String[] args) {
+ User user1 = new User("u1", "user1", "user1@gmail.com", "XXXXXXX");
+ User user2 = new User("u2", "user2", "user2@gmail.com", "XXXXXXX");
+ User user3 = new User("u3", "user3", "user3@gmail.com", "XXXXXXX");
+ User user4 = new User("u4", "user4", "user3@gmail.com", "XXXXXXX");
+
+ SplitWiseRunner runner = new SplitWiseRunner();
+ runner.addUser(user1);
+ runner.addUser(user2);
+ runner.addUser(user3);
+ runner.addUser(user4);
+
+ Scanner scan = new Scanner(System.in);
+ while (scan.hasNextLine()) {
+ String command = scan.nextLine();
+ processCommand(command, runner);
+ }
+ scan.close();
+ }
+
+ private static void processCommand(String command, SplitWiseRunner runner) {
+ String[] parts = command.split(" ");
+ String action = parts[0];
+
+ switch (action) {
+ case SHOW:
+ handleShowCommand(runner, parts);
+ break;
+ case EXPENSE:
+ handleExpenseCommand(runner, parts);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private static void handleExpenseCommand(SplitWiseRunner runner, String[] parts) {
+ String paidBy = parts[1];
+ Double amount = Double.parseDouble(parts[2]);
+
+ int len = Integer.parseInt(parts[3]);
+ List paidFor = new ArrayList<>(Arrays.asList(parts).subList(4, len + 4));
+
+ String expenseType = parts[len + 4];
+ Expense expense = runner.createExpense(amount, getSplittingStrategy(expenseType, len, parts));
+ runner.addTransaction(paidBy, paidFor, expense);
+ }
+
+ private static SplittingStrategy getSplittingStrategy(String expenseType, int numUsers, String[] parts) {
+ switch (expenseType){
+ case EQUAL:
+ return new EqualSplittingStrategy(numUsers);
+ case EXACT:
+ List splitAmt = new ArrayList<>();
+ for(int i = 0; i < numUsers; i++) {
+ splitAmt.add(Double.parseDouble(parts[numUsers + 5 + i]));
+ }
+ return new ExactSplittingStrategy(splitAmt);
+ case PERCENTAGE:
+ List splitPercentage = new ArrayList<>();
+ for(int i = 0; i < numUsers; i++) {
+ splitPercentage.add(Double.parseDouble(parts[numUsers + 5 + i]));
+ }
+ return new PercentageSplittingStrategy(splitPercentage);
+ default:
+ break;
+ }
+
+ return null;
+ }
+
+ private static void handleShowCommand(SplitWiseRunner runner, String[] parts) {
+ if (parts.length == 1) {
+ runner.getAllBalances();
+ } else {
+ runner.getUserBalance(parts[1]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/splitWise/src/main/java/org/machinecoding/models/Expense.java b/splitWise/src/main/java/org/machinecoding/models/Expense.java
new file mode 100644
index 00000000..035d9426
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/models/Expense.java
@@ -0,0 +1,21 @@
+package org.machinecoding.models;
+
+import org.machinecoding.strategy.SplittingStrategy;
+
+public class Expense {
+ private Double amount;
+ private SplittingStrategy strategy;
+
+ public Expense(Double amount, SplittingStrategy strategy) {
+ this.amount = amount;
+ this.strategy = strategy;
+ }
+
+ public SplittingStrategy getStrategy() {
+ return strategy;
+ }
+
+ public Double getAmount() {
+ return amount;
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/models/Transaction.java b/splitWise/src/main/java/org/machinecoding/models/Transaction.java
new file mode 100644
index 00000000..e0d1caae
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/models/Transaction.java
@@ -0,0 +1,43 @@
+package org.machinecoding.models;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Transaction {
+ private User paidBy;
+ private List paidFor;
+ private Expense expense;
+
+ private Map splits; // paidFor, Expense expense) {
+ this.paidBy = paidBy;
+ this.paidFor = paidFor;
+ this.expense = expense;
+ this.splits = new HashMap<>();
+ }
+
+ public void populateSplitMap() {
+ List splitAmount = expense.getStrategy().getSplit(expense.getAmount());
+ for (int i = 0; i < paidFor.size(); i++) {
+ splits.put(paidFor.get(i), splitAmount.get(i));
+ }
+ }
+
+ public Map getSplits() {
+ return splits;
+ }
+
+ public User getPaidBy() {
+ return paidBy;
+ }
+
+ public List getPaidFor() {
+ return paidFor;
+ }
+
+ public Expense getExpense() {
+ return expense;
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/models/User.java b/splitWise/src/main/java/org/machinecoding/models/User.java
new file mode 100644
index 00000000..9f053a34
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/models/User.java
@@ -0,0 +1,31 @@
+package org.machinecoding.models;
+
+public class User {
+ private final String id;
+ private final String name;
+ private final String email;
+ private final String mobileNumber;
+
+ public User(String id, String name, String email, String mobileNumber) {
+ this.mobileNumber = mobileNumber;
+ this.email = email;
+ this.name = name;
+ this.id = id;
+ }
+
+ public String getMobileNumber() {
+ return mobileNumber;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getId() {
+ return id;
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/models/UserPair.java b/splitWise/src/main/java/org/machinecoding/models/UserPair.java
new file mode 100644
index 00000000..78efe1eb
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/models/UserPair.java
@@ -0,0 +1,35 @@
+package org.machinecoding.models;
+
+import java.util.Objects;
+
+public class UserPair {
+ private User user;
+ private User friend;
+
+ public User getUser() {
+ return user;
+ }
+
+ public User getFriend() {
+ return friend;
+ }
+
+ public UserPair(User user, User friend) {
+ this.user = user;
+ this.friend = friend;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ UserPair userPair = (UserPair) o;
+ return Objects.equals(user, userPair.user) &&
+ Objects.equals(friend, userPair.friend);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(user, friend);
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/repository/BalanceRepository.java b/splitWise/src/main/java/org/machinecoding/repository/BalanceRepository.java
new file mode 100644
index 00000000..f42c3bee
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/repository/BalanceRepository.java
@@ -0,0 +1,80 @@
+package org.machinecoding.repository;
+
+import org.machinecoding.models.User;
+import org.machinecoding.models.UserPair;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class BalanceRepository {
+ private Map userBalanceMap;
+ // a b (a -> b) => a owes b
+
+ public BalanceRepository() {
+ this.userBalanceMap = new HashMap<>();
+ }
+
+ public void addUserBalance(UserPair pair, Double balance) {
+ balance = roundToTwoDecimalPlaces(balance);
+
+ if (Objects.equals(pair.getFriend(), pair.getUser())) return;
+ if (userBalanceMap.containsKey(pair)) {
+ userBalanceMap.put(pair, userBalanceMap.get(pair) + balance);
+ } else {
+ userBalanceMap.put(pair, balance);
+ }
+
+ // simplify transactions
+ UserPair pair2 = new UserPair(pair.getFriend(), pair.getUser());
+ if (userBalanceMap.containsKey(pair2)) {
+ Double amt1 = userBalanceMap.get(pair);
+ Double amt2 = userBalanceMap.get(pair2);
+
+ if (amt1 > amt2) {
+ userBalanceMap.put(pair, amt1 - amt2);
+ userBalanceMap.remove(pair2);
+ } else if (amt1 < amt2) {
+ userBalanceMap.put(pair2, amt2 - amt1);
+ userBalanceMap.remove(pair);
+ } else {
+ userBalanceMap.remove(pair);
+ userBalanceMap.remove(pair2);
+ }
+ }
+ }
+
+ private Double roundToTwoDecimalPlaces(Double value) {
+ return Math.round(value * 100.0) / 100.0;
+ }
+
+ public void getUserBalance(User user) {
+ boolean gotTransaction = false;
+ for (Map.Entry entry : userBalanceMap.entrySet()) {
+ User usr = entry.getKey().getUser();
+ User frnd = entry.getKey().getFriend();
+ if (Objects.equals(usr, user) || Objects.equals(frnd, user)) {
+ System.out.println(usr.getName() + " owes " + frnd.getName() + ": " + entry.getValue());
+ gotTransaction = true;
+ }
+ }
+
+ if (!gotTransaction) {
+ System.out.println("No balances");
+ }
+ }
+
+ public void getAllBalances() {
+ boolean gotTransaction = false;
+ for (Map.Entry entry : userBalanceMap.entrySet()) {
+ User usr = entry.getKey().getUser();
+ User frnd = entry.getKey().getFriend();
+ System.out.println(usr.getName() + " owes " + frnd.getName() + ": " + entry.getValue());
+ gotTransaction = true;
+ }
+
+ if (!gotTransaction) {
+ System.out.println("No balances");
+ }
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/repository/UserRepository.java b/splitWise/src/main/java/org/machinecoding/repository/UserRepository.java
new file mode 100644
index 00000000..c4450dee
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/repository/UserRepository.java
@@ -0,0 +1,22 @@
+package org.machinecoding.repository;
+
+import org.machinecoding.models.User;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class UserRepository {
+ private Map userMap;
+
+ public UserRepository() {
+ userMap = new HashMap<>();
+ }
+
+ public void addUser(User user) {
+ userMap.put(user.getId(), user);
+ }
+
+ public Map getUserMap() {
+ return userMap;
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/services/SplitWiseRunner.java b/splitWise/src/main/java/org/machinecoding/services/SplitWiseRunner.java
new file mode 100644
index 00000000..140cfe17
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/services/SplitWiseRunner.java
@@ -0,0 +1,56 @@
+package org.machinecoding.services;
+
+import org.machinecoding.models.*;
+import org.machinecoding.repository.BalanceRepository;
+import org.machinecoding.repository.UserRepository;
+import org.machinecoding.strategy.SplittingStrategy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class SplitWiseRunner {
+ private BalanceRepository balanceRepository;
+ private UserRepository userRepository;
+
+ public SplitWiseRunner(){
+ this.balanceRepository = new BalanceRepository();
+ this.userRepository = new UserRepository();
+ }
+
+ public void addUser(User user){
+ userRepository.addUser(user);
+ }
+
+ public Expense createExpense(Double amt, SplittingStrategy strategy){
+ return new Expense(amt, strategy);
+ }
+
+ public void addTransaction(String paidBy, List paidFor, Expense exp){
+ User paidByUser = userRepository.getUserMap().get(paidBy);
+ List paidForUsers = paidFor.stream()
+ .map(u->userRepository.getUserMap().get(u))
+ .collect(Collectors.toList());
+
+ Transaction transaction = new Transaction(paidByUser, paidForUsers, exp);
+ transaction.populateSplitMap();
+
+ addEntryInUserBalance(paidByUser, transaction);
+ }
+
+ public void addEntryInUserBalance(User paidBy, Transaction transaction) {
+ for (Map.Entry entry : transaction.getSplits().entrySet()) {
+ balanceRepository.addUserBalance(new UserPair(entry.getKey(), paidBy), entry.getValue());
+ }
+ }
+
+ public void getAllBalances() {
+ balanceRepository.getAllBalances();
+ }
+
+ public void getUserBalance(String userId) {
+ balanceRepository.getUserBalance(userRepository.getUserMap().get(userId));
+ }
+
+}
diff --git a/splitWise/src/main/java/org/machinecoding/strategy/EqualSplittingStrategy.java b/splitWise/src/main/java/org/machinecoding/strategy/EqualSplittingStrategy.java
new file mode 100644
index 00000000..5e340d98
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/strategy/EqualSplittingStrategy.java
@@ -0,0 +1,26 @@
+package org.machinecoding.strategy;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class EqualSplittingStrategy implements SplittingStrategy {
+ private final int numUsers;
+
+ public EqualSplittingStrategy(int numUsers) {
+ this.numUsers = numUsers;
+ }
+
+ @Override
+ public List getSplit(Double amount) {
+ List share = new ArrayList<>();
+ for (int i = 0; i < numUsers; i++) {
+ share.add(roundToTwoDecimalPlaces(amount / numUsers));
+ }
+ return share;
+ }
+
+ private Double roundToTwoDecimalPlaces(Double value) {
+ return Math.round(value * 100.0) / 100.0;
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/strategy/ExactSplittingStrategy.java b/splitWise/src/main/java/org/machinecoding/strategy/ExactSplittingStrategy.java
new file mode 100644
index 00000000..2ebf5c29
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/strategy/ExactSplittingStrategy.java
@@ -0,0 +1,32 @@
+package org.machinecoding.strategy;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class ExactSplittingStrategy implements SplittingStrategy{
+ List exactAmount;
+
+ public ExactSplittingStrategy(List exactAmount) {
+ this.exactAmount = exactAmount;
+ }
+
+ @Override
+ public List getSplit(Double amount) {
+ Double sum = 0.0;
+ for (Double i : exactAmount){
+ sum += i;
+ }
+
+ if(!sum.equals(amount)){
+ throw new IllegalArgumentException("Sum of the amounts should be equal to total expense");
+ }
+
+ return exactAmount.stream()
+ .map(this::roundToTwoDecimalPlaces)
+ .collect(Collectors.toList());
+ }
+
+ private Double roundToTwoDecimalPlaces(Double value) {
+ return Math.round(value * 100.0) / 100.0;
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/strategy/PercentageSplittingStrategy.java b/splitWise/src/main/java/org/machinecoding/strategy/PercentageSplittingStrategy.java
new file mode 100644
index 00000000..d4ec0f1d
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/strategy/PercentageSplittingStrategy.java
@@ -0,0 +1,32 @@
+package org.machinecoding.strategy;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class PercentageSplittingStrategy implements SplittingStrategy{
+ List percentages;
+
+ public PercentageSplittingStrategy(List percentages){
+ this.percentages = percentages;
+ }
+
+ @Override
+ public List getSplit(Double amount) {
+ Double sum = 0.0;
+ for (Double percentage : percentages) {
+ sum += percentage;
+ }
+ if(!sum.equals(100.0)){
+ throw new IllegalArgumentException("Sum of percentage must be equal to 100");
+ }
+
+ return percentages.stream()
+ .map(t -> roundToTwoDecimalPlaces(t * amount / 100.00))
+ .collect(Collectors.toList());
+ }
+
+ private Double roundToTwoDecimalPlaces(Double value) {
+ return Math.round(value * 100.0) / 100.0;
+ }
+}
diff --git a/splitWise/src/main/java/org/machinecoding/strategy/SplittingStrategy.java b/splitWise/src/main/java/org/machinecoding/strategy/SplittingStrategy.java
new file mode 100644
index 00000000..28c5bb17
--- /dev/null
+++ b/splitWise/src/main/java/org/machinecoding/strategy/SplittingStrategy.java
@@ -0,0 +1,7 @@
+package org.machinecoding.strategy;
+
+import java.util.List;
+
+public interface SplittingStrategy {
+ List getSplit(Double amount);
+}