├── .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 --------------------------------------------------------------------------------