-
-
Save daveneedstoknow/4cbb6a7d707d8efe4cb5fff1ee748923 to your computer and use it in GitHub Desktop.
Roulette Kata
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Random; | |
public class RouletteWheel { | |
private boolean spinning = false; | |
private final WheelObserver wheelObserver; | |
private long spinForMs; | |
private long currentMs = 0; | |
private final Random random = new Random(); | |
public static void spin(final WheelObserver wheelObserver, final int spinDuration) { | |
RouletteWheel rouletteWheel = new RouletteWheel(wheelObserver); | |
TickProvider tickProvider = new TickProvider(rouletteWheel); | |
tickProvider.start(); | |
rouletteWheel.spin(spinDuration); | |
} | |
RouletteWheel(final WheelObserver wheelObserver) { | |
this.wheelObserver = wheelObserver; | |
} | |
void spin(final long spinForMs) { | |
this.spinning = true; | |
this.spinForMs = spinForMs; | |
} | |
void tick(final long timeMs) { | |
currentMs = timeMs; | |
if (spinning && currentMs >= spinForMs) | |
{ | |
spinning = false; | |
final int location = random.nextInt(37); | |
this.wheelObserver.stopped(location); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.*; | |
import static org.junit.Assert.*; | |
import static org.mockito.Mockito.*; | |
import java.util.Set; | |
import java.util.HashSet; | |
public class RouletteWheelTest { | |
@Test | |
public void shouldNotifyStopped20SecondsAfterSpin() { | |
WheelObserver wheelObserver = mock(WheelObserver.class); | |
RouletteWheel wheel = new RouletteWheel(wheelObserver); | |
wheel.spin(20000); | |
wheel.tick(20000); | |
verify(wheelObserver).stopped(anyInt()); | |
} | |
@Test | |
public void shouldProvideRandomBallLocationWhenStopped() { | |
final boolean seenAll[] = new boolean[1]; | |
seenAll[0] = false; | |
WheelObserver wheelObserver = new WheelObserver() { | |
Set<Integer> seen = new HashSet<Integer>(); | |
public void stopped(final int location) { | |
if (location < 0 || location > 36) | |
throw new IllegalArgumentException(); | |
seen.add(location); | |
if (seen.size() == 37) seenAll[0] = true; | |
} | |
}; | |
RouletteWheel wheel = new RouletteWheel(wheelObserver); | |
for (int x = 0; x < 1000; x++) | |
{ | |
wheel.spin(0); | |
wheel.tick(20000); | |
} | |
assertTrue(seenAll[0]); | |
} | |
@Test | |
public void shouldSpecifyBallLocationWhenStopped() { | |
WheelObserver wheelObserver = mock(WheelObserver.class); | |
RouletteWheel wheel = new RouletteWheel(wheelObserver); | |
long spinFor20s = 20000; | |
wheel.spin(spinFor20s); | |
wheel.tick(20000); | |
verify(wheelObserver, times(1)).stopped(anyInt()); | |
} | |
@Test | |
public void shouldSpecifyBallLocationOnceWhenStopped() { | |
WheelObserver wheelObserver = mock(WheelObserver.class); | |
RouletteWheel wheel = new RouletteWheel(wheelObserver); | |
long spinFor20s = 20000; | |
wheel.spin(spinFor20s); | |
wheel.tick(20000); | |
wheel.tick(20001); | |
verify(wheelObserver, times(1)).stopped(anyInt()); | |
} | |
@Test | |
public void shouldNotNotifyStoppedBeforeSpinEnd() { | |
WheelObserver wheelObserver = mock(WheelObserver.class); | |
RouletteWheel wheel = new RouletteWheel(wheelObserver); | |
long spinFor20s = 20000; | |
wheel.spin(spinFor20s); | |
long timeEndMs = 10000; | |
wheel.tick(timeEndMs); | |
verify(wheelObserver, never() ).stopped(anyInt()); | |
} | |
@Test | |
public void shouldBuildGame() { | |
WheelObserver wheelObserver = mock(WheelObserver.class); | |
long now= System.currentTimeMillis(); | |
RouletteWheel.spin(wheelObserver, 500); | |
verify(wheelObserver, timeout(600).times(1)).stopped(anyInt()); | |
long elapsed = System.currentTimeMillis() - now; | |
assertTrue(elapsed > 480); | |
assertTrue(elapsed < 520); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Timer; | |
import java.util.TimerTask; | |
class TickProvider { | |
private final TimerTask tickTask; | |
TickProvider(final RouletteWheel rouletteWheel) | |
{ | |
this.tickTask = new TimerTask() { | |
private long startMs = System.currentTimeMillis(); | |
public void run() { | |
rouletteWheel.tick(System.currentTimeMillis() - startMs); | |
} | |
}; | |
} | |
void start() { | |
Timer timer = new Timer(); | |
timer.schedule(tickTask,0, 100); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.junit.*; | |
import static org.junit.Assert.*; | |
import static org.mockito.Mockito.*; | |
import static org.mockito.AdditionalMatchers.*; | |
import org.mockito.InOrder; | |
public class TickProviderTest { | |
@Test | |
public void shouldNotifyTickWhenStarted() { | |
RouletteWheel rouletteWheel = mock(RouletteWheel.class); | |
TickProvider tickProvider = new TickProvider(rouletteWheel); | |
tickProvider.start(); | |
verify(rouletteWheel, timeout(150).atLeastOnce()).tick(anyInt()); | |
} | |
@Test | |
public void shouldNotifyTickEvery100ms() throws Exception { | |
RouletteWheel rouletteWheel = mock(RouletteWheel.class); | |
TickProvider tickProvider = new TickProvider(rouletteWheel); | |
tickProvider.start(); | |
verify(rouletteWheel, timeout(500).atLeast(5)).tick(anyInt()); | |
} | |
@Test | |
public void shouldIncrementEachTickByApprox100() throws Exception { | |
RouletteWheel rouletteWheel = mock(RouletteWheel.class); | |
TickProvider tickProvider = new TickProvider(rouletteWheel); | |
InOrder inOrder = inOrder(rouletteWheel, | |
rouletteWheel, | |
rouletteWheel); | |
tickProvider.start(); | |
Thread.sleep(200); | |
inOrder.verify(rouletteWheel) | |
.tick(and(gt((long)0), lt((long)20))); | |
inOrder.verify(rouletteWheel) | |
.tick(and(gt((long)90), lt((long)120))); | |
inOrder.verify(rouletteWheel) | |
.tick(and(gt((long)190), lt((long)220))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I did this kata in response to a challenge set by @jjefries1
here https://docs.google.com/document/d/18mBn4R_DsuHPSuZHCqTgXrhXfyPJW-Gd46EJS_mWy2k/editand
attempted by @RonJeffries here http://ronjeffries.com/articles/016-04/roulette-1/
Blog post by me on the kata here https://thinkfoo.wordpress.com/2016/04/19/roulette-kata/