├── .gitignore
├── LICENSE
├── NOTICE
├── README.md
├── bank_ocr
├── README.md
├── java
│ ├── .gitignore
│ ├── build.gradle
│ ├── pom.xml
│ ├── settings.gradle
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── org
│ │ │ └── pivotal
│ │ │ └── katas
│ │ │ └── BankOcr.java
│ │ └── test
│ │ └── java
│ │ └── org
│ │ └── pivotal
│ │ └── katas
│ │ └── BankOcrTest.java
└── ruby
│ ├── lib
│ └── bank_ocr.rb
│ └── test
│ └── bank_ocr_test.rb
├── bowling
├── README.md
├── java
│ ├── .gitignore
│ ├── build.gradle
│ ├── pom.xml
│ ├── settings.gradle
│ └── src
│ │ ├── main
│ │ └── java
│ │ │ └── org
│ │ │ └── pivotal
│ │ │ └── katas
│ │ │ └── Bowling.java
│ │ └── test
│ │ └── java
│ │ └── org
│ │ └── pivotal
│ │ └── katas
│ │ └── BowlingTest.java
└── ruby
│ ├── lib
│ └── bowling.rb
│ └── test
│ └── bowling_test.rb
└── checkout
├── README.md
├── java
├── .gitignore
├── build.gradle
├── pom.xml
├── settings.gradle
└── src
│ ├── main
│ └── java
│ │ └── org
│ │ └── pivotal
│ │ └── katas
│ │ └── Checkout.java
│ └── test
│ └── java
│ └── org
│ └── pivotal
│ └── katas
│ └── CheckoutTest.java
└── ruby
├── lib
└── checkout.rb
└── test
└── checkout_test.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.iml
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Redistribution and use in source and binary forms, with or without
2 | modification, are permitted provided that the following conditions are
3 | met:
4 |
5 | 1. Redistributions of source code must retain the above copyright
6 | notice, this list of conditions and the following disclaimer.
7 |
8 | 2. Redistributions in binary form must reproduce the above
9 | copyright notice, this list of conditions and the following
10 | disclaimer in the documentation and/or other materials provided
11 | with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright 2022 VMware, Inc.
2 |
3 | This product is licensed to you under the BSD 2 clause (the "License"). You may not use this product except in compliance with the License.
4 |
5 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Code Katas
2 | ### What is this?
3 | These are exercises to help us practice and discuss software as a craft.
4 |
5 | #### Resources
6 | - [CodingDojo](http://codingdojo.org/)
7 | - [CodeKata](http://codekata.com/)
8 | - [Gilded Rose](https://github.com/jimweirich/gilded_rose_kata) and [presentation](https://www.youtube.com/watch?v=8bZh5LMaSmE)
9 | - [TDD Excercises](https://github.com/lucaminudel/TDDwithMockObjectsAndDesignPrinciples)
10 | - [Java Design Patterns](https://github.com/iluwatar/java-design-patterns)
11 | - [C2 Wiki](http://c2.com/cgi/wiki/FrontPage)
12 |
13 | #### Comments? Questions? Concerns?
14 | File an issue! Or contact [ahanafy@pivotal.io](mailto:ahanafy@pivotal.io) or [bkelly@pivotal.io](mailto:bkelly@pivotal.io)
--------------------------------------------------------------------------------
/bank_ocr/README.md:
--------------------------------------------------------------------------------
1 | # Code Kata - Bank OCR
2 | Originally from [CodingDojo](http://codingdojo.org/cgi-bin/index.pl?KataBankOCR)
3 |
4 | #### How to run
5 | Ruby:
6 |
7 | cd bank_ocr/ruby
8 | ruby test/bank_ocr_test.rb
9 |
10 | Java:
11 |
12 | cd bank_ocr/java
13 | mvn test
14 | # OR
15 | gradle build
16 |
17 | #### Comments? Questions? Concerns?
18 | File an issue! Or contact [ahanafy@pivotal.io](mailto:ahanafy@pivotal.io) or [bkelly@pivotal.io](mailto:bkelly@pivotal.io)
--------------------------------------------------------------------------------
/bank_ocr/java/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /build
3 | /.gradle
4 |
--------------------------------------------------------------------------------
/bank_ocr/java/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 | apply plugin: 'maven'
3 |
4 | group = 'org.pivotal.katas'
5 | version = '1.0'
6 |
7 | description = """bank-ocr"""
8 |
9 | sourceCompatibility = 1.8
10 | targetCompatibility = 1.8
11 |
12 | repositories {
13 | maven { url "http://repo.maven.apache.org/maven2" }
14 | }
15 | dependencies {
16 | testCompile group: 'junit', name: 'junit', version:'4.12'
17 | }
--------------------------------------------------------------------------------
/bank_ocr/java/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | org.pivotal.katas
5 | bank-ocr
6 | jar
7 | 1.0
8 |
9 |
10 | junit
11 | junit
12 | 4.12
13 | test
14 |
15 |
16 |
17 |
18 |
19 | org.apache.maven.plugins
20 | maven-compiler-plugin
21 | 3.5
22 |
23 | 1.8
24 | 1.8
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/bank_ocr/java/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'bank-ocr'
2 |
--------------------------------------------------------------------------------
/bank_ocr/java/src/main/java/org/pivotal/katas/BankOcr.java:
--------------------------------------------------------------------------------
1 | package org.pivotal.katas;
2 |
3 | public class BankOcr {
4 | private final String accountNumber;
5 |
6 | public BankOcr(String accountNumber) {
7 | this.accountNumber = accountNumber;
8 | }
9 |
10 | public String accountNumber() {
11 | return "";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/bank_ocr/java/src/test/java/org/pivotal/katas/BankOcrTest.java:
--------------------------------------------------------------------------------
1 | package org.pivotal.katas;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | public class BankOcrTest {
8 | @Test
9 | public void test_parsesWhenTheAccountIsValid() throws Exception {
10 | String num = " _ _ _ _ _ _ _ \n" +
11 | "| _| _||_||_ |_ ||_||_|\n" +
12 | "||_ _| | _||_| ||_| _|\n";
13 | BankOcr ocr = new BankOcr(num);
14 | assertEquals("123456789", ocr.accountNumber());
15 | }
16 |
17 | @Test
18 | public void test_parsesWhenTheAccountHasOneValidAlternative() throws Exception {
19 | String num = " _ _ _ _ _ _ _ _ \n" +
20 | " _||_||_ |_||_| _||_||_ |_ \n" +
21 | " _ | _||_||_||_ |_||_| _|\n";
22 | BankOcr ocr = new BankOcr(num);
23 | assertEquals("345882865", ocr.accountNumber());
24 | }
25 |
26 | @Test
27 | public void test_appendsIllegalStatusIfNoAlternatives() throws Exception {
28 | String num = " \n"+
29 | "|_||_||_||_||_||_||_||_||_|\n"+
30 | " | | | | | | | | |\n";
31 | BankOcr ocr = new BankOcr(num);
32 | assertEquals("444444444 ILL", ocr.accountNumber());
33 | }
34 |
35 | @Test
36 | public void test_appendsIllegalStatusIfAccountIsNotParsable() throws Exception {
37 | String num = " _ _ _ _ _ _ _ _ \n" +
38 | " _ _| _||_||_ |_ ||_||_|\n" +
39 | " _ |_ _| | _||_| ||_| _|\n";
40 | BankOcr ocr = new BankOcr(num);
41 | assertEquals("?23456789 ILL", ocr.accountNumber());
42 | }
43 |
44 | @Test
45 | public void test_appendsAmbiguityStatusIfAccountHasMultipleInterpretations() throws Exception {
46 | String num = " _ _ _ _ _ _ _ \n"+
47 | "|_||_|| || ||_ | | ||_ \n"+
48 | " | _||_||_||_| | | | _|\n";
49 | BankOcr ocr = new BankOcr(num);
50 | String accountNumber = ocr.accountNumber();
51 | assertTrue("Account number should be parsed as '490067715' and marked ambiguous", accountNumber.startsWith("490067715 AMB"));
52 | assertTrue("Account number should list '490067115' as a possibility", accountNumber.contains("490067115"));
53 | assertTrue("Account number should list '490067719' as a possibility", accountNumber.contains("490067719"));
54 | assertTrue("Account number should list '490867715' as a possibility", accountNumber.contains("490867715"));
55 | }
56 | }
--------------------------------------------------------------------------------
/bank_ocr/ruby/lib/bank_ocr.rb:
--------------------------------------------------------------------------------
1 | class BankOcr
2 | def initialize(text)
3 | @text = text
4 | end
5 |
6 | def account_number
7 | characters = @text
8 | .split("\n")
9 | .map(&:chars)
10 | .map { |c| c.each_slice(3).to_a }
11 | .transpose.map(&:flatten)
12 |
13 | numbers = characters.map do |num|
14 | case num
15 | when zero
16 | '0'
17 | when one
18 | '1'
19 | when two
20 | '2'
21 | when three
22 | '3'
23 | when four
24 | '4'
25 | when five
26 | '5'
27 | when six
28 | '6'
29 | when seven
30 | '7'
31 | when eight
32 | '8'
33 | when nine
34 | '9'
35 | else
36 | '?'
37 | end
38 | end
39 |
40 | illegal = false
41 | error = false
42 |
43 | if numbers.include?('?')
44 | illegal = true
45 | else
46 | checksum = numbers.reverse.each_with_index.reduce(0) { |acc, (num, idx)| acc + (idx + 1) * num.to_i } % 11
47 | if checksum != 0
48 | error = true
49 | end
50 | end
51 |
52 | if illegal || error
53 | differences = {}
54 | characters.each_with_index do |group, i|
55 | all.each_with_index do |num, j|
56 | if (0..9).map { |k| group[k] == num[k] ? nil : true }.compact.length == 1
57 | differences[i] ||= []
58 | differences[i] << j
59 | end
60 | end
61 | end
62 |
63 | if differences.empty?
64 | numbers.join + ' ILL'
65 | else
66 | valid = []
67 | differences.each do |i, arr|
68 | arr.each do |new|
69 | try = numbers.dup
70 | try.delete_at(i)
71 | try.insert(i, new.to_s)
72 | if try.include?('?')
73 | next
74 | else
75 | checksum = try.reverse.each_with_index.reduce(0) { |acc, (num, idx)| acc + (idx + 1) * num.to_i } % 11
76 | if checksum == 0
77 | valid << try
78 | end
79 | end
80 | end
81 | end
82 | if valid.length == 0
83 | numbers.join + ' ILL'
84 | elsif valid.length == 1
85 | valid.first.join
86 | else
87 | numbers.join + ' AMB ' + valid.map(&:join).to_s
88 | end
89 | end
90 | else
91 | numbers.join
92 | end
93 | end
94 |
95 | private
96 |
97 | def all
98 | [zero, one, two, three, four, five, six, seven, eight, nine]
99 | end
100 |
101 | def nine
102 | (' _ '+
103 | '|_|'+
104 | ' _|').chars
105 | end
106 |
107 | def eight
108 | (' _ '+
109 | '|_|'+
110 | '|_|').chars
111 | end
112 |
113 | def seven
114 | (' _ '+
115 | ' |'+
116 | ' |').chars
117 | end
118 |
119 | def six
120 | (' _ '+
121 | '|_ '+
122 | '|_|').chars
123 | end
124 |
125 | def five
126 | (' _ '+
127 | '|_ '+
128 | ' _|').chars
129 | end
130 |
131 | def four
132 | (' '+
133 | '|_|'+
134 | ' |').chars
135 | end
136 |
137 | def three
138 | (' _ '+
139 | ' _|'+
140 | ' _|').chars
141 | end
142 |
143 | def two
144 | (' _ '+
145 | ' _|'+
146 | '|_ ').chars
147 | end
148 |
149 | def one
150 | (' '+
151 | ' |'+
152 | ' |').chars
153 | end
154 |
155 | def zero
156 | (' _ '+
157 | '| |'+
158 | '|_|').chars
159 | end
160 | end
--------------------------------------------------------------------------------
/bank_ocr/ruby/test/bank_ocr_test.rb:
--------------------------------------------------------------------------------
1 | require 'minitest/autorun'
2 | require_relative '../lib/bank_ocr'
3 |
4 | describe BankOcr do
5 | describe 'parsing tests' do
6 | it 'parses an account number' do
7 | num = " _ _ _ _ _ _ _ \n" +
8 | " | _| _||_||_ |_ ||_||_|\n" +
9 | " ||_ _| | _||_| ||_| _|\n"
10 | BankOcr.new(num).account_number.must_equal('123456789')
11 | end
12 | end
13 |
14 | describe 'status tests' do
15 | it 'parses when the account has one valid alternative' do
16 | num = " _ _ _ _ _ _ _ _ \n" +
17 | " _||_||_ |_||_| _||_||_ |_ \n" +
18 | " _ | _||_||_||_ |_||_| _|\n"
19 | BankOcr.new(num).account_number.must_equal('345882865')
20 | end
21 |
22 | it 'appends illegal status if no alternatives' do
23 | num = " \n"+
24 | "|_||_||_||_||_||_||_||_||_|\n"+
25 | " | | | | | | | | |\n"
26 | BankOcr.new(num).account_number.must_equal('444444444 ILL')
27 | end
28 |
29 | it 'appends illegal status if account is not parsable' do
30 | num = " _ _ _ _ _ _ _ _ \n" +
31 | " _ _| _||_||_ |_ ||_||_|\n" +
32 | " _ |_ _| | _||_| ||_| _|\n"
33 | BankOcr.new(num).account_number.must_equal('?23456789 ILL')
34 | end
35 |
36 | it 'appends ambiguity status if account has multiple interpretations' do
37 | num = " _ _ _ _ _ _ _ \n"+
38 | "|_||_|| || ||_ | | ||_ \n"+
39 | " | _||_||_||_| | | | _|\n"
40 | account_number = BankOcr.new(num).account_number
41 | account_number.must_match (/^490067715 AMB/)
42 | account_number.must_match ('490067115')
43 | account_number.must_match ('490067719')
44 | account_number.must_match ('490867715')
45 | end
46 | end
47 | end
--------------------------------------------------------------------------------
/bowling/README.md:
--------------------------------------------------------------------------------
1 | # Code Kata - Bowling
2 | Originally from [CodingDojo](http://codingdojo.org/cgi-bin/index.pl?KataBowling)
3 |
4 | #### How to run
5 | Ruby:
6 |
7 | cd bowling/ruby
8 | ruby test/bowling_test.rb
9 |
10 | Java:
11 |
12 | cd bowling/java
13 | mvn test
14 | # OR
15 | gradle build
16 |
17 | #### Comments? Questions? Concerns?
18 | File an issue! Or contact [ahanafy@pivotal.io](mailto:ahanafy@pivotal.io) or [bkelly@pivotal.io](mailto:bkelly@pivotal.io)
--------------------------------------------------------------------------------
/bowling/java/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /build
3 | /.gradle
4 |
--------------------------------------------------------------------------------
/bowling/java/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 | apply plugin: 'maven'
3 |
4 | group = 'org.pivotal.katas'
5 | version = '1.0'
6 |
7 | description = """bowling"""
8 |
9 | sourceCompatibility = 1.8
10 | targetCompatibility = 1.8
11 |
12 | repositories {
13 | maven { url "http://repo.maven.apache.org/maven2" }
14 | }
15 | dependencies {
16 | testCompile group: 'junit', name: 'junit', version:'4.12'
17 | }
18 |
--------------------------------------------------------------------------------
/bowling/java/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | org.pivotal.katas
5 | bowling
6 | jar
7 | 1.0
8 |
9 |
10 | junit
11 | junit
12 | 4.12
13 | test
14 |
15 |
16 |
17 |
18 |
19 | org.apache.maven.plugins
20 | maven-compiler-plugin
21 | 3.5
22 |
23 | 1.8
24 | 1.8
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/bowling/java/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'bowling'
2 |
--------------------------------------------------------------------------------
/bowling/java/src/main/java/org/pivotal/katas/Bowling.java:
--------------------------------------------------------------------------------
1 | package org.pivotal.katas;
2 |
3 | public class Bowling {
4 |
5 | public void roll(int pins) {
6 |
7 | }
8 |
9 | public int score() {
10 | return 0;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/bowling/java/src/test/java/org/pivotal/katas/BowlingTest.java:
--------------------------------------------------------------------------------
1 | package org.pivotal.katas;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | public class BowlingTest {
8 | @Test
9 | public void test_correctlyCalculatesAnAllZerosGame() throws Exception {
10 | Bowling game = new Bowling();
11 |
12 | for(int i = 0; i < 9; i++) {
13 | game.roll(0);
14 | game.roll(0);
15 | }
16 |
17 | game.roll(0);
18 | game.roll(0);
19 | assertEquals(0, game.score());
20 | }
21 |
22 | @Test
23 | public void test_correctlyCalculatesAnAllOnesGame() throws Exception {
24 | Bowling game = new Bowling();
25 |
26 | for(int i = 0; i < 9; i++) {
27 | game.roll(1);
28 | game.roll(1);
29 | }
30 |
31 | game.roll(1);
32 | game.roll(1);
33 | assertEquals(20, game.score());
34 | }
35 |
36 | @Test
37 | public void test_correctlyCalculatesOneSpare() throws Exception {
38 | Bowling game = new Bowling();
39 | game.roll(3);
40 | game.roll(7);
41 | game.roll(6);
42 | assertEquals((7 + 3 + 6) + (6), game.score());
43 | }
44 |
45 | @Test
46 | public void test_correctlyCalculatesOneStrike() throws Exception {
47 | Bowling game = new Bowling();
48 | game.roll(10);
49 | game.roll(7);
50 | game.roll(1);
51 | assertEquals((10 + 7 + 1) + (7 + 1), game.score());
52 | }
53 |
54 | @Test
55 | public void test_correctlyCalculatesAPerfectGame() throws Exception {
56 | Bowling game = new Bowling();
57 |
58 | for(int i = 0; i < 9; i++) {
59 | game.roll(10);
60 | }
61 |
62 | game.roll(10);
63 | game.roll(10);
64 | game.roll(10);
65 | assertEquals(300, game.score());
66 | }
67 |
68 | @Test
69 | public void test_correctlyCalculatesAGameOfSpares() throws Exception {
70 | Bowling game = new Bowling();
71 |
72 | for(int i = 0; i < 9; i++) {
73 | game.roll(6);
74 | game.roll(4);
75 | }
76 |
77 | game.roll(6);
78 | game.roll(4);
79 | game.roll(6);
80 | assertEquals(160, game.score());
81 | }
82 | }
--------------------------------------------------------------------------------
/bowling/ruby/lib/bowling.rb:
--------------------------------------------------------------------------------
1 | class Bowling
2 | def initialize
3 | @rolls = []
4 | end
5 |
6 | def score
7 | score = 0
8 | idx = 0
9 | (0...10).each do
10 | if @rolls[idx].to_i == 10
11 | score += 10 + @rolls[idx + 1].to_i + @rolls[idx + 2].to_i
12 | idx += 1
13 | elsif @rolls[idx].to_i + @rolls[idx + 1].to_i == 10
14 | score += 10 + @rolls[idx + 2].to_i
15 | idx += 2
16 | else
17 | score += @rolls[idx].to_i + @rolls[idx + 1].to_i
18 | idx += 2
19 | end
20 | end
21 | score
22 | end
23 |
24 | def roll(pins)
25 | @rolls << pins
26 | end
27 | end
--------------------------------------------------------------------------------
/bowling/ruby/test/bowling_test.rb:
--------------------------------------------------------------------------------
1 | require 'minitest/autorun'
2 | require_relative '../lib/bowling'
3 |
4 | describe Bowling do
5 | it 'correctly calculates an all-zeros game' do
6 | game = Bowling.new
7 |
8 | 9.times do
9 | game.roll(0)
10 | game.roll(0)
11 | end
12 |
13 | game.roll(0)
14 | game.roll(0)
15 | game.score.must_equal(0)
16 | end
17 |
18 | it 'correctly calculates an all-ones game' do
19 | game = Bowling.new
20 |
21 | 9.times do
22 | game.roll(1)
23 | game.roll(1)
24 | end
25 |
26 | game.roll(1)
27 | game.roll(1)
28 | game.score.must_equal(20)
29 | end
30 |
31 | it 'correctly calculates one spare' do
32 | game = Bowling.new
33 | game.roll(3)
34 | game.roll(7)
35 | game.roll(6)
36 | game.score.must_equal((3 + 7 + 6) + (6))
37 | end
38 |
39 | it 'correctly calculates one strike' do
40 | game = Bowling.new
41 | game.roll(10)
42 | game.roll(7)
43 | game.roll(1)
44 | game.score.must_equal((10 + 7 + 1) + (7 + 1))
45 | end
46 |
47 | it 'correctly calculates a perfect game' do
48 | game = Bowling.new
49 |
50 | 9.times do
51 | game.roll(10)
52 | end
53 |
54 | game.roll(10)
55 | game.roll(10)
56 | game.roll(10)
57 | game.score.must_equal(300)
58 | end
59 |
60 | it 'correctly calculates a game of spares' do
61 | game = Bowling.new
62 |
63 | 9.times do
64 | game.roll(6)
65 | game.roll(4)
66 | end
67 |
68 | game.roll(6)
69 | game.roll(4)
70 | game.roll(6)
71 | game.score.must_equal(160)
72 | end
73 | end
--------------------------------------------------------------------------------
/checkout/README.md:
--------------------------------------------------------------------------------
1 | # Code Kata - Checkout
2 | Originally from [CodeKata](http://codekata.com/kata/kata09-back-to-the-checkout)
3 |
4 | #### How to run
5 | Ruby:
6 |
7 | cd checkout/ruby
8 | ruby test/checkout_test.rb
9 |
10 | Java:
11 |
12 | cd checkout/java
13 | mvn test
14 | # OR
15 | gradle build
16 |
17 | #### Comments? Questions? Concerns?
18 | File an issue! Or contact [ahanafy@pivotal.io](mailto:ahanafy@pivotal.io) or [bkelly@pivotal.io](mailto:bkelly@pivotal.io)
--------------------------------------------------------------------------------
/checkout/java/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /build
3 | /.gradle
4 |
--------------------------------------------------------------------------------
/checkout/java/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 | apply plugin: 'maven'
3 |
4 | group = 'org.pivotal.katas'
5 | version = '1.0'
6 |
7 | description = """checkout"""
8 |
9 | sourceCompatibility = 1.8
10 | targetCompatibility = 1.8
11 |
12 | repositories {
13 | maven { url "http://repo.maven.apache.org/maven2" }
14 | }
15 | dependencies {
16 | testCompile group: 'junit', name: 'junit', version:'4.12'
17 | }
18 |
--------------------------------------------------------------------------------
/checkout/java/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | org.pivotal.katas
5 | checkout
6 | jar
7 | 1.0
8 |
9 |
10 | junit
11 | junit
12 | 4.12
13 | test
14 |
15 |
16 |
17 |
18 |
19 | org.apache.maven.plugins
20 | maven-compiler-plugin
21 | 3.5
22 |
23 | 1.8
24 | 1.8
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/checkout/java/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'checkout'
2 |
--------------------------------------------------------------------------------
/checkout/java/src/main/java/org/pivotal/katas/Checkout.java:
--------------------------------------------------------------------------------
1 | package org.pivotal.katas;
2 |
3 | import java.util.Map;
4 |
5 | public class Checkout {
6 | private final Map rules;
7 |
8 | public Checkout(Map rules) {
9 | this.rules = rules;
10 | }
11 |
12 | public void scan(String item) {
13 |
14 | }
15 |
16 | public int total() {
17 | return 0;
18 | }
19 |
20 | public static class Discount {
21 | private final int quantity;
22 | private final int price;
23 |
24 | public Discount(int quantity, int price) {
25 | this.quantity = quantity;
26 | this.price = price;
27 | }
28 | }
29 |
30 | public static class Pricing {
31 | private final int price;
32 | private final Discount discount;
33 |
34 | public Pricing(int price) {
35 | this(price, null);
36 | }
37 |
38 | public Pricing(int price, Discount discount) {
39 | this.price = price;
40 | this.discount = discount;
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/checkout/java/src/test/java/org/pivotal/katas/CheckoutTest.java:
--------------------------------------------------------------------------------
1 | package org.pivotal.katas;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | public class CheckoutTest {
11 | private static final Map RULES = new HashMap() {{
12 | put("A", new Checkout.Pricing(50, new Checkout.Discount(3, 130)));
13 | put("B", new Checkout.Pricing(30, new Checkout.Discount(2, 45)));
14 | put("C", new Checkout.Pricing(20));
15 | put("D", new Checkout.Pricing(15));
16 | }};
17 |
18 | private int priceOf(String goods) {
19 | Checkout checkout = new Checkout(RULES);
20 | for(String item : goods.split("")) {
21 | checkout.scan(item);
22 | }
23 | return checkout.total();
24 | }
25 |
26 | @Test
27 | public void test_calculatesTotalsCorrectly() throws Exception {
28 | assertEquals(0, priceOf(""));
29 | assertEquals(50, priceOf("A"));
30 | assertEquals(80, priceOf("AB"));
31 | assertEquals(115, priceOf("CDBA"));
32 |
33 | assertEquals(100, priceOf("AA"));
34 | assertEquals(130, priceOf("AAA"));
35 | assertEquals(180, priceOf("AAAA"));
36 | assertEquals(230, priceOf("AAAAA"));
37 | assertEquals(260, priceOf("AAAAAA"));
38 |
39 | assertEquals(160, priceOf("AAAB"));
40 | assertEquals(175, priceOf("AAABB"));
41 | assertEquals(190, priceOf("AAABBD"));
42 | assertEquals(190, priceOf("DABABA"));
43 | }
44 |
45 | @Test
46 | public void test_calculatesTotalsIncrementally() throws Exception {
47 | Checkout checkout = new Checkout(RULES);
48 | assertEquals(0, checkout.total());
49 | checkout.scan("A"); assertEquals(50, checkout.total());
50 | checkout.scan("B"); assertEquals(80, checkout.total());
51 | checkout.scan("A"); assertEquals(130, checkout.total());
52 | checkout.scan("A"); assertEquals(160, checkout.total());
53 | checkout.scan("B"); assertEquals(175, checkout.total());
54 | }
55 | }
--------------------------------------------------------------------------------
/checkout/ruby/lib/checkout.rb:
--------------------------------------------------------------------------------
1 | class Checkout
2 | def initialize(rules)
3 | @rules = rules
4 | @items = []
5 | end
6 |
7 | def scan(item)
8 | @items << item
9 | end
10 |
11 | def total
12 | @grouped = @items.reduce(Hash.new(0)) do |hash, item|
13 | hash[item] += 1
14 | hash
15 | end
16 |
17 | @grouped.reduce(0) do |total, (item, quantity)|
18 | rule = @rules[item]
19 | price, discount = rule
20 | if discount
21 | dquantity, dprice = discount
22 | total += dprice * (quantity / dquantity).floor
23 | quantity = quantity % dquantity
24 | end
25 | total + price * quantity
26 | end
27 | end
28 | end
--------------------------------------------------------------------------------
/checkout/ruby/test/checkout_test.rb:
--------------------------------------------------------------------------------
1 | require 'minitest/autorun'
2 | require_relative '../lib/checkout'
3 |
4 | describe Checkout do
5 | RULES = {
6 | 'A' => [50, [3, 130]],
7 | 'B' => [30, [2, 45]],
8 | 'C' => [20],
9 | 'D' => [15]
10 | }
11 |
12 | def price_of(goods)
13 | checkout = Checkout.new(RULES)
14 | goods.split(//).each { |item| checkout.scan(item) }
15 | checkout.total
16 | end
17 |
18 | it 'calculates totals correctly' do
19 | price_of('').must_equal(0)
20 | price_of('A').must_equal(50)
21 | price_of('AB').must_equal(80)
22 | price_of('CDBA').must_equal(115)
23 |
24 | price_of('AA').must_equal(100)
25 | price_of('AAA').must_equal(130)
26 | price_of('AAAA').must_equal(180)
27 | price_of('AAAAA').must_equal(230)
28 | price_of('AAAAAA').must_equal(260)
29 |
30 | price_of('AAAB').must_equal(160)
31 | price_of('AAABB').must_equal(175)
32 | price_of('AAABBD').must_equal(190)
33 | price_of('DABABA').must_equal(190)
34 | end
35 |
36 | it 'calculates totals incrementally' do
37 | co = Checkout.new(RULES)
38 | co.total.must_equal(0)
39 | co.scan('A'); co.total.must_equal(50)
40 | co.scan('B'); co.total.must_equal(80)
41 | co.scan('A'); co.total.must_equal(130)
42 | co.scan('A'); co.total.must_equal(160)
43 | co.scan('B'); co.total.must_equal(175)
44 | end
45 | end
--------------------------------------------------------------------------------