# Paste: PONG with internal words

Author: dharmatech factor Sat, 22 Nov 2008 23:02:00
Plain Text |
```USING: accessors kernel locals math math.order namespaces
ui.render ;

IN: pong-internal

! This version demonstrates the use of "internal words".
!
! Internal words are introduced using the '[wlet' form.
!
! With code which does not utilize locals, you only name top level
! words. And your factors are in terms of these only.
!
! With locals, you name intermedia values. Internal words let you
! factor out expressions involving these named intermediate values.
!
! Take a look at this version which uses locals, but whos main body is
! totally unfactored:
!
!     http://paste.factorcode.org/paste?id=195
!
! Now consider some expressions that we factored.
!
! This bit of code establishes four new internal words:
!
!       [wlet | ball-to-left?  [ ( x -- ? ) PX > ]
!               ball-to-right? [ ( x -- ? ) PX < ]
!               ball-below?    [ ( y -- ? ) PY > ]
!               ball-above?    [ ( y -- ? ) PY < ]
!
! They simply give clearer names to expressions involving the
! position of the ball.
!
! So this expression which requires a bit of thought:
!
!     HEIGHT PY <  0 VY <  PLAYER PX <  PX PLAYER PADDLE-SIZE + <
!
! Can now be:
!
!     HEIGHT               ball-above?
!     VY 0 >
!     PLAYER               ball-to-right?
!     PLAYER PADDLE-SIZE + ball-to-left?   and and and
!
! The factoring doesn't have to stop there
! (although the code below does). That second expression could be named:
!
!     ball-moving-up? [ VY 0 > ]
!
! We could give the paddle edges more symmetrical names. And write a
! word for checking if the ball is "between" two values. Then our
! expression would become:
!
!     HEIGHT                   ball-above?
!                              ball-moving-up?
!     PLAYER-LEFT PLAYER-RIGHT ball-between?
!
! So you can take factoring as far as you want to.
!
! Here's a more advanced example.
!
! Here's the code for when the ball bounces off the player's paddle:
!
!     VY neg BOUNCE * MAX-VELOCITY min     VY! ! reverse vertical velocity
!     PX PLAYER PADDLE-SIZE 2 / + - 0.25 * VX! ! reverse horizontal velocity
!     HEIGHT PY!
!
! And the code for bouncing off the computer's paddle:
!
!     VY neg BOUNCE * MAX-VELOCITY min       VY! ! reverse vertical velocity
!     PX COMPUTER PADDLE-SIZE 2 / + - 0.25 * VX! ! reverse horizontal velocity
!     0 PY!
!
! We've got some code duplication here. Let's take it line by line.
!
! The first step in each of those blocks can be factored out:
!
!     reverse-vertical-velocity [ ( -- ) VY neg BOUNCE * MAX-VELOCITY min VY! ]
!
! The second step abstracts over the paddle's x position:
!
!     reverse-horizontal-velocity [ ( x -- ) PADDLE-SIZE 2 / + PX swap - 0.25 * VX! ]
!
! Now we can write a new word in terms of those factors:
!
!       reverse-vertical-velocity
!       reverse-horizontal-velocity
!       PY! ]
!
! So the two original blocks are now simply (respectively):
!
!
!
! One of the core principles of the Factor programming language is to
! factor code as much as possible into short words. As you can see
! here, using locals does not prevent the application of this
! principle. In fact, factoring is right at home in a lexical
! environment, if you have the right constructs, such as let and wlet.

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

: mouse-x ( -- x ) hand-loc get first ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

TUPLE: <pong> < gadget draw closed ;

M: <pong> pref-dim*    ( <pong> -- dim ) drop { 400 400 } ;
M: <pong> draw-gadget* ( <pong> --     ) draw>> call ;
M: <pong> ungraft*     ( <pong> --     ) t >>closed drop ;

! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

: play-pong ( -- )

[let | PONG [ <pong> new-gadget ] |

PONG

[let | WIDTH         [ 400   ]
HEIGHT        [ 400   ]
BALL-DIAMETER [  20   ]
VX!           [   3   ] ! BALL VELOCITY
VY!           [   4   ]
PX!           [  50   ] ! BALL POSITION
PY!           [  50   ]
BOUNCE        [   1.2 ]
COMPUTER!     [ 200   ]
COMPSPEED!    [  10   ]
PLAYER!       [   f   ]
MAX-VELOCITY  [  10   ] |

[wlet | ball-to-left?  [ ( x -- ? ) PX > ]
ball-to-right? [ ( x -- ? ) PX < ]
ball-below?    [ ( y -- ? ) PY > ]
ball-above?    [ ( y -- ? ) PY < ]

reverse-vertical-velocity   [ ( -- )   VY neg BOUNCE * MAX-VELOCITY min VY!   ]
reverse-horizontal-velocity [ ( x -- ) PADDLE-SIZE 2 / + PX swap - 0.25 * VX! ] |

reverse-vertical-velocity
reverse-horizontal-velocity
PY! ]                       |

[
-50 ball-above?   HEIGHT 50 + ball-below?   and
[

mouse-x 0 max  WIDTH PADDLE-SIZE -  min  PLAYER!

PX VX + PX!    PY VY + PY!

COMPUTER PADDLE-SIZE + ball-to-right? [ COMPUTER COMPSPEED + COMPUTER! ] when
COMPUTER               ball-to-left?  [ COMPUTER COMPSPEED - COMPUTER! ] when

! check if player blocked ball
HEIGHT ball-above?   VY 0 >   PLAYER ball-to-right?   PLAYER PADDLE-SIZE + ball-to-left?   and and and
when

! check if computer blocked ball
PY 0 <  VY 0 <  COMPUTER PX <  PX COMPUTER PADDLE-SIZE + <   and and and
when

! bounce off walls
0 ball-to-left?   WIDTH ball-to-right?   or [ VX neg VX! ] when

PLAYER   HEIGHT 4 -  PADDLE-SIZE 4 rectangle*

{ PX PY } BALL-DIAMETER circle
]
when
] ] ] ]

>>draw drop

PONG "test" open-window

[
[
PONG closed>>
[ f ]
[ PONG relayout-1 25 sleep t ]
if
]
loop
]