Re: Factor

Factor: the language, the theory, and the practice.

Color Picker Game

Wednesday, May 3, 2023

In the SwiftUI by Tutorials book by Ray Wenderlich, there is a tutorial on building RGBullsEye, which is a game for adjusting RGB Colors using sliders to match a provided random color and providing a “color score” to the user showing how well they matched it. Some users have even posted their solutions on GitHub.

I thought it would be fun to build a version of this example using Factor.

We could generate a color by using random-unit to make three random values for the `red`, `green`, and `blue` slots. Instead, we can pick randomly from the standard color database.

``````: random-color ( -- color )
named-colors random named-color ;
``````

Comparing two colors can use the rgba-distance word from the colors.distances vocabulary, returning an integer score out of 100 points:

``````: color-score ( color1 color2 -- n )
rgba-distance 1.0 swap - 100.0 * round >integer ;
``````

We can define a gadget type that can be used to find our object in a gadget hierarchy.

``````TUPLE: color-picker-game < track ;
``````

Given a child of the `color-picker-game` instance, we can pull out the `color-preview` gadgets in a slightly fragile way by knowing where they are in the layout:

``````: find-color-previews ( gadget -- preview1 preview2 )
[ color-picker-game? ] find-parent
children>> first children>> first2 ;
``````

Using that, we can make a button that, when clicked:

1. finds the two `color-preview` objects
2. grabs the latest color value from their models
3. calculates the “color score”
4. displays it by modifying the button text
``````: <match-button> ( -- button )
"Match Color" [
dup find-color-previews
[ model>> compute-model ] bi@
over children>> first text<< relayout
] <border-button> ;
``````

Another button can be used to reset the color we are trying to match against to a new random color, setting it on the model used by the left `color-preview`:

``````: <reset-button> ( -- button )
"Random" [
find-color-previews drop model>>
random-color swap set-model
] <border-button> ;
``````

Using these two buttons, and some gadgets from the color picker vocabulary, we can build up our interface, choosing a random color to start, and then laying out the other components we need:

``````:: <color-picker-game> ( -- gadget )
vertical color-picker-game new-track { 5 5 } >>gap

random-color <model>     :> left-model
\ <rgba> <color-sliders> :> ( sliders right-model )

horizontal <track>

``````MAIN-WINDOW: color-picker-game-window