From 13de36168c73351dffaf7e8d736e8416faaf5f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=80=E7=94=B0=20=E7=BE=A9=E8=A3=95?= Date: Thu, 7 Nov 2013 21:07:04 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=E5=9B=9E=E7=AD=94=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code_breaker/kameda/codebreaker.rb | 48 ++++++++++++++++ .../codebreaker_submits_guess.feature | 57 +++++++++++++++++++ .../step_definitions/codebreaker_steps.rb | 41 +++++++++++++ code_breaker/kameda/features/support/env.rb | 6 ++ 4 files changed, 152 insertions(+) create mode 100644 code_breaker/kameda/codebreaker.rb create mode 100644 code_breaker/kameda/features/codebreaker_submits_guess.feature create mode 100644 code_breaker/kameda/features/step_definitions/codebreaker_steps.rb create mode 100644 code_breaker/kameda/features/support/env.rb diff --git a/code_breaker/kameda/codebreaker.rb b/code_breaker/kameda/codebreaker.rb new file mode 100644 index 0000000..ab58b2f --- /dev/null +++ b/code_breaker/kameda/codebreaker.rb @@ -0,0 +1,48 @@ +# -*- encoding: utf-8 -*- + + +class Codebreaker + class Game + def initialize(output) + @output = output + end + + def start(secret) + @secret = secret + end + + def guess(guess) + secret_ary = @secret.split(//) + guess_ary = guess.split(//) + pluses, others = secret_ary.zip(guess_ary).partition {|sec, gue| + sec == gue + } + plus_count = pluses.size + + other_secret_hash = Hash.new {|h,k| h[k] = 0} + other_guesses = [] + others.each {|sec, gue| + other_secret_hash[sec] += 1 + other_guesses << gue + } + minus_count = 0 + other_guesses.each {|gue| + if other_secret_hash[gue] > 0 + minus_count += 1 + other_secret_hash[gue] -= 1 + end + } + ('+' * plus_count) + ('-' * minus_count) + end + + end +end + +case $0 +when __FILE__ + main +when /spec[^\/]*$/ + # {spec of the implementation} +end + + diff --git a/code_breaker/kameda/features/codebreaker_submits_guess.feature b/code_breaker/kameda/features/codebreaker_submits_guess.feature new file mode 100644 index 0000000..2cd941d --- /dev/null +++ b/code_breaker/kameda/features/codebreaker_submits_guess.feature @@ -0,0 +1,57 @@ +Feature: codebreaker submits guess + The codebreaker submits a guess of four numbers. The game marks the guess with + and - signs. + + For each number in the guess that matches the number and position of a number in the secret code, the mark includes one + sign. For each number in the guess that matches the number but not the position of a number in the secret code, the mark includes one - sign. + + Each position in the secret code can only be matched once. For example, a guess of 1134 against a secret code of 1234 would get three plus signs: one for each of the exact matches in the first, third and fourth positions. The number match in the second position would be ignored. + + Scenario Outline: submit a guess + Given the secret code is "" + When I guess "" + Then the mark should be "" + + Scenarios: no matches + | code | guess | mark | + | 1234 | 5555 | | + + Scenarios: 1 number correct + | code | guess | mark | + | 1234 | 1555 | + | + | 1234 | 2555 | - | + + Scenarios: 2 numbers correct + | code | guess | mark | + | 1234 | 5254 | ++ | + | 1234 | 5154 | +- | + | 1234 | 2545 | -- | + + Scenarios: 3 numbers correct + | code | guess | mark | + | 1234 | 5234 | +++ | + | 1234 | 5134 | ++- | + | 1234 | 5124 | +-- | + | 1234 | 5123 | --- | + + Scenarios: all numbers correct + | code | guess | mark | + | 1234 | 1234 | ++++ | + | 1234 | 1243 | ++-- | + | 1234 | 1423 | +--- | + | 1234 | 4321 | ---- | + + Scenarios: matches with duplicates + | code | guess | mark | + | 1234 | 1155 | + | + | 1234 | 5115 | - | + | 1134 | 1155 | ++ | + | 1134 | 5115 | +- | + | 1134 | 5511 | -- | + | 1134 | 1115 | ++ | + | 1134 | 5111 | +- | + | 1155 | 1234 | + | + | 1111 | 1112 | +++ | + | 1113 | 1121 | ++- | + | 3111 | 1311 | ++-- | + | 3114 | 1251 | -- | + | 1511 | 2134 | - | + diff --git a/code_breaker/kameda/features/step_definitions/codebreaker_steps.rb b/code_breaker/kameda/features/step_definitions/codebreaker_steps.rb new file mode 100644 index 0000000..ee81d31 --- /dev/null +++ b/code_breaker/kameda/features/step_definitions/codebreaker_steps.rb @@ -0,0 +1,41 @@ +# -*- encoding: utf-8 -*- + +Given /^I am not yet playing$/ do +end + +When /^I start a new game$/ do + game = Codebreaker::Game.new(output) + game.start('1234') +end + +Then /^I should see "([^\"]*)"$/ do |message| + output.messages.should include(message) +end + +Given /^the secret code is "([^\"]*)"$/ do |secret| + @game = Codebreaker::Game.new(output) + @game.start(secret) +end + +When /^I guess "([^\"]*)"$/ do |guess| + @guess = guess +end + +Then /^the mark should be "([^\"]*)"$/ do |mark| + @game.guess(@guess).should == mark +end + +class Output + def messages + @messages ||= [] + end + + def puts(message) + messages << message + end +end + +def output + @output ||= Output.new +end + diff --git a/code_breaker/kameda/features/support/env.rb b/code_breaker/kameda/features/support/env.rb new file mode 100644 index 0000000..1e71fbe --- /dev/null +++ b/code_breaker/kameda/features/support/env.rb @@ -0,0 +1,6 @@ + + + +$LOAD_PATH << File.expand_path('../..', File.dirname(__FILE__)) +require 'codebreaker' + From f4c8ba85dc68ed95d9f46e6aa0b09f34d25f188d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=80=E7=94=B0=20=E7=BE=A9=E8=A3=95?= Date: Thu, 7 Nov 2013 21:22:07 +0900 Subject: [PATCH 2/7] fix caller --- code_breaker/kameda/codebreaker.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code_breaker/kameda/codebreaker.rb b/code_breaker/kameda/codebreaker.rb index ab58b2f..7a4944d 100644 --- a/code_breaker/kameda/codebreaker.rb +++ b/code_breaker/kameda/codebreaker.rb @@ -18,13 +18,13 @@ def guess(guess) sec == gue } plus_count = pluses.size - other_secret_hash = Hash.new {|h,k| h[k] = 0} other_guesses = [] others.each {|sec, gue| other_secret_hash[sec] += 1 other_guesses << gue } + minus_count = 0 other_guesses.each {|gue| if other_secret_hash[gue] > 0 @@ -40,7 +40,11 @@ def guess(guess) case $0 when __FILE__ - main + game = Codebreaker::Game.new(STDOUT) + game.start('1234') + while(code=gets.chomp) + puts game.guess(code) + end when /spec[^\/]*$/ # {spec of the implementation} end From 62349a85b67a277df01eeee51c8ce1d7bfc408c0 Mon Sep 17 00:00:00 2001 From: Yoshihiro Kameda Date: Fri, 8 Nov 2013 14:11:48 +0900 Subject: [PATCH 3/7] =?UTF-8?q?module=E4=BD=BF=E3=81=86=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- code_breaker/kameda/codebreaker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code_breaker/kameda/codebreaker.rb b/code_breaker/kameda/codebreaker.rb index 7a4944d..5f9bf96 100644 --- a/code_breaker/kameda/codebreaker.rb +++ b/code_breaker/kameda/codebreaker.rb @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- -class Codebreaker +module Codebreaker class Game def initialize(output) @output = output From 908c2b3ccfd67317669032fc04ce0e3abc51827f Mon Sep 17 00:00:00 2001 From: Yoshihiro Kameda Date: Fri, 8 Nov 2013 14:13:31 +0900 Subject: [PATCH 4/7] refactoring:rename --- code_breaker/kameda/codebreaker.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code_breaker/kameda/codebreaker.rb b/code_breaker/kameda/codebreaker.rb index 5f9bf96..6af6ecd 100644 --- a/code_breaker/kameda/codebreaker.rb +++ b/code_breaker/kameda/codebreaker.rb @@ -14,10 +14,10 @@ def start(secret) def guess(guess) secret_ary = @secret.split(//) guess_ary = guess.split(//) - pluses, others = secret_ary.zip(guess_ary).partition {|sec, gue| + matches, others = secret_ary.zip(guess_ary).partition {|sec, gue| sec == gue } - plus_count = pluses.size + match_count = matches.size other_secret_hash = Hash.new {|h,k| h[k] = 0} other_guesses = [] others.each {|sec, gue| @@ -25,14 +25,14 @@ def guess(guess) other_guesses << gue } - minus_count = 0 + hit_count = 0 other_guesses.each {|gue| if other_secret_hash[gue] > 0 - minus_count += 1 + hit_count += 1 other_secret_hash[gue] -= 1 end } - ('+' * plus_count) + ('-' * minus_count) + ('+' * match_count) + ('-' * hit_count) end end From 6094bbec6e82c8c0ef3da26dd410191e6580f5a6 Mon Sep 17 00:00:00 2001 From: Yoshihiro Kameda Date: Fri, 8 Nov 2013 14:30:32 +0900 Subject: [PATCH 5/7] refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit real_hit_count = total_hit_count - match_count で求めるようにした。 --- code_breaker/kameda/codebreaker.rb | 42 +++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/code_breaker/kameda/codebreaker.rb b/code_breaker/kameda/codebreaker.rb index 6af6ecd..b4bf6b5 100644 --- a/code_breaker/kameda/codebreaker.rb +++ b/code_breaker/kameda/codebreaker.rb @@ -14,25 +14,31 @@ def start(secret) def guess(guess) secret_ary = @secret.split(//) guess_ary = guess.split(//) - matches, others = secret_ary.zip(guess_ary).partition {|sec, gue| - sec == gue - } - match_count = matches.size - other_secret_hash = Hash.new {|h,k| h[k] = 0} - other_guesses = [] - others.each {|sec, gue| - other_secret_hash[sec] += 1 - other_guesses << gue - } - - hit_count = 0 - other_guesses.each {|gue| - if other_secret_hash[gue] > 0 - hit_count += 1 - other_secret_hash[gue] -= 1 + + match_count = self.match_count(secret_ary, guess_ary) + total_hit_count = self.total_hit_count(secret_ary, guess_ary) + real_hit_count = total_hit_count - match_count + + ('+' * match_count) + ('-' * real_hit_count) + end + + def total_hit_count(secret_ary, guess_ary_org) + guess_ary = guess_ary_org.dup + secret_ary.select {|char| + i = guess_ary.index(char) + if i + guess_ary.delete_at(i) + true + else + false end - } - ('+' * match_count) + ('-' * hit_count) + }.size + end + + def match_count(secret_ary, guess_ary) + secret_ary.zip(guess_ary).select {|sec, gue| + sec == gue + }.size end end From 0ac54cb8e612c7732d116afe246edcb48e5ad7b9 Mon Sep 17 00:00:00 2001 From: Yoshihiro Kameda Date: Fri, 8 Nov 2013 14:47:16 +0900 Subject: [PATCH 6/7] refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit total_hit_countを書きなおした --- code_breaker/kameda/codebreaker.rb | 34 ++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/code_breaker/kameda/codebreaker.rb b/code_breaker/kameda/codebreaker.rb index b4bf6b5..f520934 100644 --- a/code_breaker/kameda/codebreaker.rb +++ b/code_breaker/kameda/codebreaker.rb @@ -1,6 +1,18 @@ # -*- encoding: utf-8 -*- +module Enumerable + def sum + self.inject(0) {|memo,i| memo + i} + end +end + +class Object + def try(message) + self.nil? ? nil : self.send(message) + end +end + module Codebreaker class Game def initialize(output) @@ -16,23 +28,19 @@ def guess(guess) guess_ary = guess.split(//) match_count = self.match_count(secret_ary, guess_ary) - total_hit_count = self.total_hit_count(secret_ary, guess_ary) - real_hit_count = total_hit_count - match_count + real_hit_count = self.total_hit_count(secret_ary, guess_ary) - match_count ('+' * match_count) + ('-' * real_hit_count) end - def total_hit_count(secret_ary, guess_ary_org) - guess_ary = guess_ary_org.dup - secret_ary.select {|char| - i = guess_ary.index(char) - if i - guess_ary.delete_at(i) - true - else - false - end - }.size + def total_hit_count(secret_ary, guess_ary) + secret_hash = secret_ary.group_by {|char| char} # {要素 => 要素数} というハッシュを作りたい + guess_hash = guess_ary.group_by {|char| char} + secret_hash.map {|key, chars| + secret_count = chars.size + guess_count = guess_hash[key].try(:size).to_i + [secret_count, guess_count].min + }.sum end def match_count(secret_ary, guess_ary) From abdf193df25a91d0c6389fc29e85fee17c94ff02 Mon Sep 17 00:00:00 2001 From: Yoshihiro Kameda Date: Fri, 8 Nov 2013 14:48:52 +0900 Subject: [PATCH 7/7] trivial --- code_breaker/kameda/codebreaker.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code_breaker/kameda/codebreaker.rb b/code_breaker/kameda/codebreaker.rb index f520934..a2b57c6 100644 --- a/code_breaker/kameda/codebreaker.rb +++ b/code_breaker/kameda/codebreaker.rb @@ -34,11 +34,11 @@ def guess(guess) end def total_hit_count(secret_ary, guess_ary) - secret_hash = secret_ary.group_by {|char| char} # {要素 => 要素数} というハッシュを作りたい - guess_hash = guess_ary.group_by {|char| char} + secret_hash = secret_ary.group_by {|char| char} # {要素 => 要素数} というハッシュを作りたいが、次善策 + guess_hash = guess_ary .group_by {|char| char} secret_hash.map {|key, chars| secret_count = chars.size - guess_count = guess_hash[key].try(:size).to_i + guess_count = guess_hash[key].try(:size).to_i [secret_count, guess_count].min }.sum end