Skip to content

Commit 9effc7c

Browse files
authored
Merge pull request #17073 from etrandafir93/features/BAEL-8198-data_oriented_programming
BAEL-8198: data oriented programming
2 parents 6e9b565 + d4ced1f commit 9effc7c

File tree

9 files changed

+296
-0
lines changed

9 files changed

+296
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
6+
<modelVersion>4.0.0</modelVersion>
7+
<artifactId>data-oriented-programming</artifactId>
8+
<version>1.0</version>
9+
<name>Data-Oriented Programming</name>
10+
<description>Project for Data Oriented Programming in Java</description>
11+
12+
<parent>
13+
<groupId>com.baeldung</groupId>
14+
<artifactId>patterns-modules</artifactId>
15+
<version>1.0.0-SNAPSHOT</version>
16+
</parent>
17+
18+
<build>
19+
<plugins>
20+
<plugin>
21+
<groupId>org.apache.maven.plugins</groupId>
22+
<artifactId>maven-compiler-plugin</artifactId>
23+
<configuration>
24+
<source>21</source>
25+
<target>21</target>
26+
<!-- Needed due to a bug with JDK 21, described here: https://issues.apache.org/jira/browse/MCOMPILER-546?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&focusedCommentId=17767513 -->
27+
<debug>false</debug>
28+
<compilerArgs>
29+
<arg>--enable-preview</arg>
30+
</compilerArgs>
31+
</configuration>
32+
</plugin>
33+
<plugin>
34+
<groupId>org.apache.maven.plugins</groupId>
35+
<artifactId>maven-surefire-plugin</artifactId>
36+
<configuration>
37+
<argLine>--enable-preview</argLine>
38+
</configuration>
39+
</plugin>
40+
41+
</plugins>
42+
</build>
43+
44+
<properties>
45+
<maven.compiler.source.version>21</maven.compiler.source.version>
46+
<maven.compiler.target.version>21</maven.compiler.target.version>
47+
</properties>
48+
49+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.baeldung.patterns;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
import java.util.Optional;
6+
7+
import static java.util.Comparator.reverseOrder;
8+
import static java.util.function.Function.identity;
9+
import static java.util.stream.Collectors.counting;
10+
import static java.util.stream.Collectors.groupingBy;
11+
12+
public class ScoringRules {
13+
14+
private ScoringRules() {
15+
}
16+
17+
static int pairs(List<Integer> dices, int nrOfPairs) {
18+
Map<Integer, Long> frequency = dices.stream()
19+
.collect(groupingBy(identity(), counting()));
20+
21+
List<Integer> pairs = frequency
22+
.entrySet().stream()
23+
.filter(it -> it.getValue() >= 2)
24+
.map(Map.Entry::getKey)
25+
.toList();
26+
27+
if (pairs.size() < nrOfPairs) {
28+
return 0;
29+
}
30+
31+
return pairs.stream()
32+
.sorted(reverseOrder())
33+
.limit(nrOfPairs)
34+
.mapToInt(it -> it * 2)
35+
.sum();
36+
}
37+
38+
static Integer moreOfSameKind(List<Integer> roll, int nrOfDicesOfSameKind) {
39+
Map<Integer, Long> frequency = roll.stream()
40+
.collect(groupingBy(identity(), counting()));
41+
42+
Optional<Integer> diceValue = frequency.entrySet().stream()
43+
.filter(entry -> entry.getValue() >= nrOfDicesOfSameKind)
44+
.map(Map.Entry::getKey)
45+
.max(Integer::compare);
46+
47+
return diceValue.map(it -> it * nrOfDicesOfSameKind)
48+
.orElse(0);
49+
}
50+
51+
static Integer specificValue(List<Integer> dices, Integer value) {
52+
return dices.stream()
53+
.filter(value::equals)
54+
.mapToInt(it -> it)
55+
.sum();
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.baeldung.patterns;
2+
3+
4+
import com.baeldung.patterns.data.Roll;
5+
import com.baeldung.patterns.data.Strategies;
6+
import com.baeldung.patterns.data.Strategies.*;
7+
import com.baeldung.patterns.data.Strategy;
8+
import com.baeldung.patterns.data.Turn;
9+
10+
import java.util.ArrayList;
11+
import java.util.HashSet;
12+
import java.util.List;
13+
import java.util.concurrent.ThreadLocalRandom;
14+
import java.util.function.Supplier;
15+
import java.util.stream.IntStream;
16+
17+
import static com.baeldung.patterns.ScoringRules.*;
18+
19+
public class Yahtzee {
20+
21+
private Yahtzee() {
22+
}
23+
24+
static Supplier<Integer> diceValueGenerator = () -> ThreadLocalRandom.current().nextInt(1, 7);
25+
26+
static Roll roll() {
27+
List<Integer> dice = IntStream.rangeClosed(1, 5)
28+
.mapToObj(__ -> randomDieValue())
29+
.toList();
30+
return new Roll(dice, 1);
31+
}
32+
33+
static Roll rerollValues(Roll roll, Integer... values) {
34+
List<Integer> valuesToReroll = new ArrayList<>(List.of(values));
35+
if (roll.rollCount() >= 3) {
36+
throw new IllegalStateException("You can re-roll 3 times at most.");
37+
}
38+
if (!new HashSet<>(roll.dice()).containsAll(valuesToReroll)) {
39+
throw new IllegalStateException("You can re-roll dice values which are not from the original roll.");
40+
}
41+
List<Integer> newDice = roll.dice().stream().map(it -> {
42+
if (!valuesToReroll.contains(it)) {
43+
return it;
44+
}
45+
valuesToReroll.remove(it);
46+
return randomDieValue();
47+
}).toList();
48+
49+
return new Roll(newDice, roll.rollCount() + 1);
50+
}
51+
52+
static Turn chooseStrategy(Roll roll, String strategyStr) {
53+
Strategy strategy = Strategies.fromString(strategyStr);
54+
return new Turn(roll, strategy);
55+
}
56+
57+
static int score(Turn turn) {
58+
var dices = turn.roll().dice();
59+
return switch (turn.strategy()) {
60+
case Ones __ -> specificValue(dices, 1);
61+
case Twos __ -> specificValue(dices, 2);
62+
case OnePair __ -> pairs(dices, 1);
63+
case TwoPairs __ -> pairs(dices, 2);
64+
case ThreeOfaKind __ -> moreOfSameKind(dices, 3);
65+
};
66+
}
67+
68+
private static Integer randomDieValue() {
69+
return diceValueGenerator.get();
70+
}
71+
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.baeldung.patterns.data;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
6+
public record Roll(List<Integer> dice, int rollCount) {
7+
public Roll {
8+
if (dice.size() != 5)
9+
throw new IllegalArgumentException("A Roll needs to have exactly 5 dice.");
10+
if (dice.stream().anyMatch(die -> die < 1 || die > 6))
11+
throw new IllegalArgumentException("Dice values should be between 1 and 6.");
12+
13+
dice = Collections.unmodifiableList(dice);
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.baeldung.patterns.data;
2+
3+
public class Strategies {
4+
public record Ones() implements Strategy {
5+
}
6+
7+
public record Twos() implements Strategy {
8+
}
9+
10+
public record OnePair() implements Strategy {
11+
}
12+
13+
public record TwoPairs() implements Strategy {
14+
}
15+
16+
public record ThreeOfaKind() implements Strategy {
17+
}
18+
19+
public static Strategy fromString(String strategyString) {
20+
return switch (strategyString) {
21+
case "ONES" -> new Ones();
22+
case "TWOS" -> new Twos();
23+
case "ONE_PAIR" -> new OnePair();
24+
case "TWO_PAIRS" -> new TwoPairs();
25+
case "THREE_OF_A_KIND" -> new ThreeOfaKind();
26+
default -> throw new IllegalStateException("Unknown strategy: " + strategyString);
27+
};
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.baeldung.patterns.data;
2+
3+
import static com.baeldung.patterns.data.Strategies.*;
4+
5+
public sealed interface Strategy permits Ones, Twos, OnePair, TwoPairs, ThreeOfaKind {
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.baeldung.patterns.data;
2+
3+
public record Turn(Roll roll, Strategy strategy) {
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.baeldung.patterns;
2+
3+
import com.baeldung.patterns.data.Roll;
4+
import com.baeldung.patterns.data.Turn;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.params.ParameterizedTest;
7+
import org.junit.jupiter.params.provider.Arguments;
8+
import org.junit.jupiter.params.provider.MethodSource;
9+
10+
import java.util.List;
11+
import java.util.stream.Stream;
12+
13+
import static com.baeldung.patterns.Yahtzee.*;
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
16+
class YahtzeeTest {
17+
18+
public static Stream<Arguments> whenThePlayerChoosesAStrategy_thenCalculateCorrectScore() {
19+
return Stream.of(
20+
Arguments.of(List.of(3, 3, 3, 4, 4), "ONE_PAIR", 8),
21+
Arguments.of(List.of(3, 3, 3, 4, 4), "THREE_OF_A_KIND", 9),
22+
Arguments.of(List.of(1, 2, 2, 4, 4), "ONE_PAIR", 8),
23+
Arguments.of(List.of(1, 2, 2, 2, 5), "THREE_OF_A_KIND", 6),
24+
Arguments.of(List.of(1, 1, 1, 1, 5), "ONE_PAIR", 2),
25+
Arguments.of(List.of(1, 1, 1, 1, 5), "THREE_OF_A_KIND", 3)
26+
);
27+
}
28+
29+
@ParameterizedTest
30+
@MethodSource
31+
void whenThePlayerChoosesAStrategy_thenCalculateCorrectScore(List<Integer> dices, String strategyStr, Integer expectedScore) {
32+
enqueueFakeDiceValues(dices);
33+
34+
Roll roll = roll();
35+
Turn play = chooseStrategy(roll, strategyStr);
36+
int score = score(play);
37+
38+
assertEquals(expectedScore, score);
39+
}
40+
41+
@Test
42+
void whenThePlayerRerollsAndChoosesTwoPairs_thenCalculateCorrectScore() {
43+
enqueueFakeDiceValues(1, 1, 2, 2, 3, 5, 5);
44+
45+
Roll roll = roll(); // => { dice: [1,1,2,2,3] }
46+
roll = rerollValues(roll, 1, 1); // => { dice: [5,5,2,2,3] }
47+
Turn turn = chooseStrategy(roll, "TWO_PAIRS");
48+
int score = score(turn);
49+
50+
assertEquals(14, score);
51+
}
52+
53+
private static void enqueueFakeDiceValues(List<Integer> values) {
54+
Yahtzee.diceValueGenerator = values.iterator()::next;
55+
}
56+
57+
private static void enqueueFakeDiceValues(Integer... values) {
58+
enqueueFakeDiceValues(List.of(values));
59+
}
60+
61+
}

patterns-modules/pom.xml

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
<module>solid</module>
3838
<module>monkey-patching</module>
3939
<module>vertical-slice-architecture</module>
40+
41+
<!-- this commented out since the module needs Java 21 -->
42+
<!-- <module>data-oriented-programming</module>-->
4043
</modules>
4144

4245
</project>

0 commit comments

Comments
 (0)