Skip to content

Commit

Permalink
Harry Potter implemented with Spock
Browse files Browse the repository at this point in the history
  • Loading branch information
Christoph Welcz committed Apr 4, 2022
1 parent edfd9fa commit 7c87ef2
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 0 deletions.
1 change: 1 addition & 0 deletions java/harry-potter-spock/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
8 changes: 8 additions & 0 deletions java/harry-potter-spock/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions java/harry-potter-spock/.idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions java/harry-potter-spock/.idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions java/harry-potter-spock/.idea/markdown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions java/harry-potter-spock/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 51 additions & 0 deletions java/harry-potter-spock/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>harry-potter-java</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>io.vavr</groupId>
<artifactId>vavr</artifactId>
<version>0.10.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>2.1-groovy-3.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.12.1</version>
<executions>
<execution>
<goals>
<goal>compileTests</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
58 changes: 58 additions & 0 deletions java/harry-potter-spock/src/main/java/HarryPotter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import io.vavr.Function1;
import io.vavr.collection.List;
import io.vavr.collection.Traversable;

import java.math.BigDecimal;

public class HarryPotter {
public static BigDecimal calculatePrice(List<Integer> books) {
var distinctBooks = countDistinctBooks(books);
var bundlesGreedy = bundleGreedy(distinctBooks);
var bundlesAdjusted = adjustBundles(bundlesGreedy);
return (BigDecimal) bundlesAdjusted.map(HarryPotter::giveDiscount).sum();
}

public static List<Integer> countDistinctBooks(List<Integer> books) {
return books.groupBy(Function1.identity())
.mapValues(Traversable::size)
.values()
.sorted()
.toList();
}

public static List<Integer> bundleGreedy(List<Integer> bookSets) {
return bookSets
.scanLeft(bookSets, (remainingBooks, __) -> removeOneBundle(remainingBooks))
.filter(Traversable::nonEmpty)
.map(Traversable::size);
}

private static List<Integer> removeOneBundle(List<Integer> remainingBooks) {
return remainingBooks.map(it -> it - 1).filter(it -> it > 0);
}

public static List<Integer> adjustBundles(List<Integer> greedyBundles) {
var howManyThrees = greedyBundles.count(it -> it == 3);
var howManyFives = greedyBundles.count(it -> it == 5);
var commonCount = Math.min(howManyThrees, howManyFives);
var bundlesOfFour = List.fill(commonCount * 2, 4);
var remainingBundlesOfThree = List.fill(howManyThrees - commonCount, 3);
var remainingBundlesOfFive = List.fill(howManyFives - commonCount, 5);
return greedyBundles.reject(it -> it == 3)
.reject(it -> it == 5)
.appendAll(bundlesOfFour)
.appendAll(remainingBundlesOfThree)
.appendAll(remainingBundlesOfFive);
}

public static BigDecimal giveDiscount(int bundleSize) {
return new BigDecimal[]{
BigDecimal.valueOf(8),
BigDecimal.valueOf(15.2),
BigDecimal.valueOf(21.6),
BigDecimal.valueOf(25.6),
BigDecimal.valueOf(30.0)
}[bundleSize - 1];
}
}

88 changes: 88 additions & 0 deletions java/harry-potter-spock/src/test/groovy/HarryPotterTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import io.vavr.collection.List
import spock.lang.Specification

class HarryPotterTest extends Specification {
def "integration test"() {
when: "price is calculated"
def price = HarryPotter.calculatePrice(toVavr(books))

then: "expected price is achieved"
price == expected

where:
books | expected
[1, 2, 3, 4, 5] | 30.00
[1, 1, 2, 2, 3, 3, 4, 5] | 51.20
}

def "count books"() {
when: "distinct books are counted"
def result = HarryPotter.countDistinctBooks(toVavr(books))

then: "expected count is returned"
result == toVavr(expected)

where:
books | expected
[] | []
[1] | [1]
[1, 1] | [2]
[1, 1, 1] | [3]
[1, 1, 1, 2, 2] | [2, 3]
[1, 1, 2, 2, 3, 3, 4, 5] | [1, 1, 2, 2, 2]
}

def "build greedy bundles"() {
when: "count of books are bundled"
def greedyBundles = HarryPotter.bundleGreedy(toVavr(count))

then: "expected bundles are achieved"
greedyBundles == toVavr(expected)

where:
count | expected
[1, 1, 2, 2, 2] | [5, 3]
[2, 2, 2, 2, 2] | [5, 5]
[2, 2, 2, 3, 3] | [5, 5, 2]
[2, 2, 2] | [3, 3]
[1, 1, 1] | [3]
}

def "adjusts bundles swapping 5,3 to 4,4"() {
when: "greedy bundles are adjusted to achieve highest discount"
def greedyBundles = HarryPotter.adjustBundles(toVavr(bundles))

then: "expected bundles are achieved"
greedyBundles == toVavr(expected)

where:
bundles | expected
[] | []
[1, 1, 1] | [1, 1, 1]
[1, 1, 3] | [1, 1, 3]
[5, 3] | [4, 4]
[5, 3, 3] | [4, 4, 3]
[5, 3, 3, 5] | [4, 4, 4, 4]
[5, 3, 3, 5, 3] | [4, 4, 4, 4, 3]
}

def "gives discounts for sets"() {
when: "discount is calculated"
def result = HarryPotter.giveDiscount(bundleSize)

then: "expected discount is returned"
result == expected

where:
bundleSize | expected
1 | 1.0 * 8
2 | 0.95 * 2 * 8
3 | 0.90 * 3 * 8
4 | 0.80 * 4 * 8
5 | 0.75 * 5 * 8
}

List<Integer> toVavr(java.util.List<Integer> xs) {
List.ofAll(xs)
}
}

0 comments on commit 7c87ef2

Please sign in to comment.