Ricochet Play
by Stan Munson
Welcome to Ricochet Play … or Propel the Shell?
Use HOME to reset the turtle position.
Use RIGHT or LEFT or SETHEADING to aim.
Use S with a number to make a shot.
Get fancy and try a bank shot.
RicochetPlay.lgo
; Ricochet Play takes advantage of the BOUNCE mode of the graphics window.
; When the turtle hits the edge, it 'bounces off the wall' at an angle
; of reflection that simulates a pool ball hitting the rail.
TO SET.THE.TABLE
DRAW
(SETSHAPE)
SETTS 1
MAKE "MAXX FIRST BOUNDS
MAKE "MAXY LAST BOUNDS
SETBG "GREEN
SETPC "DARKGREEN
; Attempt to make a 'rail'
SETPC "LIGHTGREEN BORDER 10
SETBOUNDS LIST (FIRST BOUNDS) - 10 (LAST BOUNDS) - 10
MAKE "MAXX FIRST BOUNDS
MAKE "MAXY LAST BOUNDS
; Pocket centers: 4 corners and 2 side pockets
MAKE "LLC LIST MINUS :MAXX MINUS :MAXY
MAKE "ULC LIST MINUS :MAXX :MAXY
MAKE "TSP LIST 0 :MAXY + 10
MAKE "URC LIST :MAXX :MAXY
MAKE "LRC LIST :MAXX MINUS :MAXY
MAKE "BSP LIST 0 MINUS :MAXY + 10
; Alternate side pockets if table is vertical
; Additional side pockets if table is square
MAKE "RSP LIST :MAXX + 10 0
MAKE "LSP LIST MINUS :MAXX + 10 0
; Pocket names are in a sequence that reflects the orientation of the table
; so that the sights can properly be marked on the rails.
; The orientation also determines where the spots are located.
; Make a note of the orientation -- assume it's horizontal but adjust for vertical.
; A square table is a special case since all 4 sides have side pockets.
COND [
[.EQ :MAXX :MAXY][MAKE "POCKETS [LLC LSP ULC TSP URC RSP LRC BSP] MAKE "ORIENTATION "SQUARE]
[.GT :MAXX :MAXY][MAKE "POCKETS [LLC ULC TSP URC LRC BSP] MAKE "ORIENTATION "HORIZONTAL]
[.LT :MAXX :MAXY][MAKE "POCKETS [LLC LSP ULC URC RSP LRC] MAKE "ORIENTATION "VERTICAL]
["TRUE][(THROW MINUS 42 `Life is not as we know it`)]
]
MAKE "RADIUS 25
SETPC "BLACK
HT
; Draw the pockets
FOREACH :POCKETS [
PU SETXY THING "? PD (STAMPOVAL :RADIUS :RADIUS "TRUE)
]
MAKE "HORIZONTAL.SPOTS LIST (LIST -.50 * :MAXX 0) (LIST .50 * :MAXX 0)
MAKE "VERTICAL.SPOTS LIST (LIST 0 .50 * :MAXY) (LIST 0 -.50 * :MAXY)
MAKE "HORIZONTAL.SIGHTS (LIST
(LIST MINUS :MAXX + 5 -.50 * :MAXY)
(LIST MINUS :MAXX + 5 0)
(LIST MINUS :MAXX + 5 .50 * :MAXY)
(LIST -.75 * :MAXX :MAXY + 5)
(LIST -.50 * :MAXX :MAXY + 5)
(LIST -.25 * :MAXX :MAXY + 5)
(LIST .25 * :MAXX :MAXY + 5)
(LIST .50 * :MAXX :MAXY + 5)
(LIST .75 * :MAXX :MAXY + 5)
(LIST :MAXX + 5 .50 * :MAXY)
(LIST :MAXX + 5 0)
(LIST :MAXX + 5 -.50 * :MAXY)
(LIST .75 * :MAXX MINUS :MAXY + 5)
(LIST .50 * :MAXX MINUS :MAXY + 5)
(LIST .25 * :MAXX MINUS :MAXY + 5)
(LIST -.25 * :MAXX MINUS :MAXY + 5)
(LIST -.50 * :MAXX MINUS :MAXY + 5)
(LIST -.75 * :MAXX MINUS :MAXY + 5)
)
MAKE "VERTICAL.SIGHTS (LIST
(LIST MINUS :MAXX + 5 -.75 * :MAXY)
(LIST MINUS :MAXX + 5 -.50 * :MAXY)
(LIST MINUS :MAXX + 5 -.25 * :MAXY)
(LIST MINUS :MAXX + 5 .25 * :MAXY)
(LIST MINUS :MAXX + 5 .50 * :MAXY)
(LIST MINUS :MAXX + 5 .75 * :MAXY)
(LIST -.50 * :MAXX :MAXY + 5)
(LIST 0 :MAXY + 5)
(LIST .50 * :MAXX :MAXY + 5)
(LIST :MAXX + 5 .75 * :MAXY)
(LIST :MAXX + 5 .50 * :MAXY)
(LIST :MAXX + 5 .25 * :MAXY)
(LIST :MAXX + 5 -.25 * :MAXY)
(LIST :MAXX + 5 -.50 * :MAXY)
(LIST :MAXX + 5 -.75 * :MAXY)
(LIST .50 * :MAXX MINUS :MAXY + 5)
(LIST 0 MINUS :MAXY + 5)
(LIST -.50 * :MAXX MINUS :MAXY + 5)
)
; Draw the spots and sights ... don't use black since that is the color of the pockets.
SWITCH :ORIENTATION [
[HORIZONTAL] [DO.SPOTS :HORIZONTAL.SPOTS DO.SIGHTS :HORIZONTAL.SIGHTS]
[VERTICAL] [DO.SPOTS :VERTICAL.SPOTS DO.SIGHTS :VERTICAL.SIGHTS]
[SQUARE][]
]
PU HOME ST
CT
BOUNCE
PRINT `Welcome to Ricochet Play ... or Propel the Shell?`
PRINT ``
PRINT `Use HOME reset the turtle position.`
PRINT `Use RIGHT or LEFT or SETHEADING to aim.`
PRINT `Use S with a number to make a shot.`
PRINT `Get fancy and try a bank shot.`
PRINT ``
PRINT `To try different table sizes, drag the windows around,`
PRINT `then type T.`
END
TO DO.SPOTS :SPOTS
FOREACH :SPOTS [
PU SETXY ? PD SETPC "WHITE (STAMPOVAL 5 5 "TRUE) SETPC "DARKGRAY (STAMPOVAL 2 2 "TRUE)
]
END
TO DO.SIGHTS :SIGHTS
FOREACH :SIGHTS [
PU SETXY ? PD SETPC "GOLDENROD (STAMPOVAL 3 3 "TRUE)
]
END
; It's already been determined that the turtle is in a pocket.
; POCKET outputs the pocket name where the turtle is.
TO POCKET
FOREACH :POCKETS [
IF INCIRCLE? THING "? OUTPUT "?
]
; (THROW MINUS 42 `There must have been dirt on the table`)
; Believe it or don't, the stamped circle that represents the pocket
; can be ever so slightly beyond the radius so that the turtle thinks
; it has hit a pocket when in fact it has not so I'm treating that as
; if it was dirt on the table ... tough play, I know.
WAIT 1000
PRINT `Hold on there ... so close ... but no cigar ... tough break.`
MAKE "DIRT.ON.THE.TABLE POS
OUTPUT "DIRT.ON.THE.TABLE
END
; Uses the turtle's position to compute distance to each pocket.
; The turtle must be within :RADIUS steps from the pocket center.
; Allow for quirkiness of turtle circles that may be just bit jagged.
TO INCIRCLE? :CENTER
IF .LE DISTANCE :CENTER (:RADIUS + 1) OUTPUT "TRUE
OUTPUT "FALSE
END
TO IN.POCKET?
FOREACH :POCKETS [
IF INCIRCLE? THING "? OUTPUT "TRUE
]
OUTPUT "FALSE
END
; make a shot for a distance
TO S :D
WHILE [.GT :D 0] [
FD 2 SPIN
MAKE "D :D - 2
IF IN.POCKET? [
PRINT `You're a real hustler, aren't you?`
SETXY THING POCKET
SETH TOWARDS [0 0]
(SETSHAPE)
STOP
]
]
(SETSHAPE)
PRINT `Better luck next time, rookie!`
END
TO SPIN
local "before make "before pos
local "beforeh make "beforeh heading
REPEAT 8 [RT 90]
; Michael adds :EPSILON as a range of equality for testing. Originally, SPIN would report a difference although quite small.
; Even though this is a global system variable, I set it here because this was the cause for adding it to Logo anyway.
MAKE "EPSILON .01 ; effectively a range of error of 1% will be consider equal
local "after make "after pos
local "afterh make "afterh heading
if not equal? :before :after [(show `POS before and after SPIN:` :before :after)]
if not equal? :beforeh :afterh [(show `HEADING before and after SPIN:` :beforeh :afterh)]
END
TO T
SET.THE.TABLE
END
TO DEMO
LOCALMAKE "OLDPEN PEN
REPEAT 2 [
SETH RANDOM 360 S 500 + RANDOM 1000
WAIT 1000
PU HOME RUN (LIST :OLDPEN)
]
END
; COND simulates the COND statement of Lisp
; It's an alternative to stacking up IF ... ELSE IF ... statements.
; The actual conditions are set up in the call to COND as a list of pairs of lists. For example:
; The first element of the :CONDITIONS list is a logical test and if
; it is "TRUE then the second element is run. Otherwise the next pair
; of elements is evaluated in the same way. Use "TRUE in the final
; pair to establish a default.
TO COND :CONDITIONS
IF EMPTY? :CONDITIONS STOP
IF EQUAL? "TRUE RUN FIRST :CONDITIONS THEN RUN FIRST BUTFIRST :CONDITIONS STOP
COND BUTFIRST BUTFIRST :CONDITIONS
END
BURY [COND]
TO SWITCH :VALUE :CASES
IF EMPTY? :CASES STOP
IF MEMBER? :VALUE FIRST :CASES \
THEN RUN FIRST BUTFIRST :CASES STOP
IF EQUAL? "ELSE FIRST FIRST :CASES \
THEN RUN FIRST BUTFIRST :CASES STOP
SWITCH :VALUE BUTFIRST BUTFIRST :CASES
END
BURY [SWITCH]
; This routine draws a border around the current graphics window
TO BORDER :WIDTH
(LOCAL "OLDWIDTH "OLDMODE "OLDPEN "OLDPOS "OLDHEADING)
MAKE "OLDWIDTH WIDTH ; NOTE: This is the Logo command not the input!
MAKE "OLDHEADING HEADING
; The pen width does not wrap around the screen so
; set the width to twice the requested size. The turtle
; is pushed up against the screen border so only half of
; its width will actually draw.
SETW 2 * :WIDTH ; NOTE: This is the input not the Logo command!
MAKE "OLDMODE GPROP :CURRENT.GRAPHICS "WRAPMODE
MAKE "OLDPEN PEN
MAKE "OLDPOS POS
; Brute force method of drawing a border ... run the turtle into the
; walls and turn right; you'd expect the turtle to be inside the fence.
FENCE PU HOME
FD 10000 RT 90 ; top edge point right
FD 10000 RT 90 ; upper right corner pointing down
PD
FD 10000 RT 90 ; lower right corner pointing left
FD 10000 RT 90 ; lower left corner pointing up
FD 10000 RT 90 ; upper left corner pointing right
FD 10000 RT 90 ; back to the upper right and all done
PU
; Restore the turtle, the pen and the wrapmode
SETPOS :OLDPOS
RUN (LIST :OLDPEN)
SETHEADING :OLDHEADING
SETW :OLDWIDTH
PPROP :CURRENT.GRAPHICS "WRAPMODE :OLDMODE
END
BURY [BORDER]
TO MAIN
SET.THE.TABLE
DEMO
END
TO ABOUT
IGNORE ALERT `Ricochet Play takes advantage of the BOUNCE mode of the Graphics Window.`
END
MAIN
Procedure | MAIN |
Description | Practicing angles in Logo"s BOUNCE mode |
Level | Intermediate |
Tags | Game, Angles, Math |