Skip to content

Instantly share code, notes, and snippets.

@timyates
Last active December 30, 2015 11:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save timyates/7823738 to your computer and use it in GitHub Desktop.
Save timyates/7823738 to your computer and use it in GitHub Desktop.
Reactive Rock Paper Scissors with Groovy and RxJava
@Grab( 'com.netflix.rxjava:rxjava-groovy:0.15.1' )
import groovy.transform.*
import rx.*
@CompileStatic
class RPSRX {
enum Result {
WIN, LOSE, DRAW
}
enum Move {
ROCK( SCISSORS ),
PAPER( ROCK ),
SCISSORS( PAPER )
Move beats
Move( Move beats ) {
this.beats = beats
}
boolean beats( Move other ) {
other == beats
}
}
@Immutable
static class PlayerMove {
String player
Move move
String toString() { "$player:$move" }
}
final Random rnd = new Random()
private <T> Observable.OnSubscribeFunc<T> subscriptionBuilder( Closure<T> generator ) {
{ Observer<T> observer ->
boolean running = true
new Thread( { ->
while( running ) {
observer.onNext( generator() )
}
observer.onCompleted();
} ).start()
return { -> running = false } as Subscription
}
}
Observable<Integer> numbers() {
int n = 1
Observable.create( subscriptionBuilder( { -> n++ } ) )
}
Observable<Move> moves() {
Observable.create( subscriptionBuilder {
Move.values()[ rnd.nextInt( Move.values().length ) ]
} )
}
Observable<PlayerMove> player( String name ) {
moves().map { Move it -> new PlayerMove( name, it ) }
}
String message( Integer index, PlayerMove m1, PlayerMove m2 ) {
"Game $index : $m1 vs $m2 : ${resultMessage( m1, m2, result( m1, m2 ) )}"
}
Result result( PlayerMove m1, PlayerMove m2 ) {
m1.move == m2.move ? Result.DRAW : m1.move.beats == m2.move ? Result.WIN : Result.LOSE
}
String resultMessage( PlayerMove m1, PlayerMove m2, Result r ) {
r == Result.DRAW ? 'draw' : r == Result.WIN ? "$m1.player wins" : "$m2.player wins"
}
Observable<String> game( String name1, String name2 ) {
Observable.zip( numbers(), player( name1 ), player( name2 ) ) { Integer n, PlayerMove a, PlayerMove b ->
message( n, a, b )
}
}
}
new RPSRX().game( 'tim', 'alice' ).take( 10 ).subscribe { println it }
@tednaleid
Copy link

Fun reactive script! One thing I've noticed about the rx-java stuff is that it's maven dependency specifies that it wants groovy-all version "2.+". Grape's Grab annotation gets very sad when given ranges, especially on groovy-all. I've found that it speeds up my testing/script execution time a lot to also specify an explicit version of groovy so that grape doesn't try to resolve every valid version of groovy every time the script executes, by adding this after your first @Grab:

@Grab('org.codehaus.groovy:groovy-all:2.2.1')

That speeds the execution time on my machine from about 20 seconds down to ~2 (without having to mess with ttl values in my ~/.groovy/grapeConfig.xml file).

@timyates
Copy link
Author

timyates commented Dec 6, 2013

@tednaleid Cool! Top tip :-) I hadn't noticed that in the rx-java deps, thanks! :-)

Rock Paper Scissors is my new Hello World ;-) Got my groovy-stream version, a Java 8 one, this one and next I might try project reactor ;-)

@rgrinberg
Copy link

Doesn't seem to work anymore:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
/Users/rgrinberg/reps/snippets/groovy/rx-rps.groovy: 34: [Static type checking] - Cannot return value of type groovy.lang.Closure <rx.Subscription> on method returning type rx.Observable$OnSubscribeFunc <T>
 @ line 34, column 13.
               { Observer<T> observer ->
               ^

1 error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment