Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Is it possible to make the computer game "pong" in stata?

    I'm trying to make a (simplified) version of the 1972 arcade game "Pong" in Stata but I've run into some difficulties. However, I'm not making pong in Stata because it is easy, but because it is hard.

    The idea is write the program entirely in ado, and to use Stata's plotting library for the UI. The first sticking point is that the high level graphics function is too slow to capture the motion of the paddle and ball. I tried to draw the plot on the first iteration, then use 'display' to update the plot as the objects "move". It redraws much faster this way, but I don't appear to be able to use display to change the coordinates of the plotted objects. graph7 is fast enough for a flickery but functional UI prototype, but I don't like that its out of date. Still, I may be able to figure out how to consume the low level graphics stuff later.

    The second sticking point is that there doesn't appear to be a good way to get Stata native keyboard interrupts for user input. Getting input from the keyboard or mouse seems much more difficult. I thought about writing a plugin for this but it would be cheating.

    Any advice? The current source covers a few files and is a bit too much to post here but I'd gladly put it up on github if there were some interest. Everyone needs a hobby, right?

  • #2
    Have you looked into the plot function (help plot)? It can provide the speed you'll require for 'gaming'.
    Last edited by Andrew Lover; 09 Mar 2020, 02:33.
    __________________________________________________ __
    Assistant Professor, Department of Biostatistics and Epidemiology
    School of Public Health and Health Sciences
    University of Massachusetts- Amherst

    Comment


    • #3
      Excellent, thanks Andrew, I think you've saved this project!

      Plot is definitely fast enough, and so so very stylish. Plus, if I can keep window focus on the stata console I can use -input- for user control and keep my pure ado implementation.

      The more I think about it, the more I like it.

      Comment


      • #4
        This is interesting, but -help plot()- didn't reveal anything for me (v. 15), nor did a search of the v.16 documentation online. Can either of you give a pointer here?

        Comment


        • #5
          Mike and I were mislead by Andrew's reference to plot as a function - it is an (out-of-date) command, and typing help plot as Andrew actually wrote, rather than help plot() as Mike and I did to find help for a plot() function, will give more details on this appealingly retro command. I can only imagine the look on my colleague's face when I send the next set of charts he requests. Now if I can just find some blue-bar 8½ by 11 paper for my laser printer ... .

          Comment


          • #6
            Quite right, plot is a depreciated command in stata 8. It draws the plot as ASCII text art. I'd love to see the output on a dot matrix printer, but unfortunately for my purposes I don't think I'll quite get the frame rate I'm looking for.
            Last edited by Daniel Schaefer; 09 Mar 2020, 13:48.

            Comment


            • #7
              Originally posted by William Lisowski View Post
              Mike and I were mislead by Andrew's reference to plot as a function - it is an (out-of-date) command, and typing help plot as Andrew actually wrote, rather than help plot() as Mike and I did to find help for a plot() function, will give more details on this appealingly retro command. I can only imagine the look on my colleague's face when I send the next set of charts he requests. Now if I can just find some blue-bar 8½ by 11 paper for my laser printer ... .
              using tractor-feed paper, of course!
              __________________________________________________ __
              Assistant Professor, Department of Biostatistics and Epidemiology
              School of Public Health and Health Sciences
              University of Massachusetts- Amherst

              Comment


              • #8
                Well, yes, of course tractor-feed, but I always use the paper with perforations between the holes and the body of the paper, so I can tear off the edges and have neat letter-sized output to attach to the memos I send to my colleagues.
                Click image for larger version

Name:	plot.png
Views:	1
Size:	55.3 KB
ID:	1540509

                Last edited by William Lisowski; 09 Mar 2020, 14:56.

                Comment


                • #9
                  It's amusing (possibly amazing, depending on your age and memory) to recall a generation of texts and papers using line printer graphics like this with not just no apology but indeed an implicit boast "I used a computer for my plots" and -- hint, hint -- I don't need to do it myself by hand with a special pen etc. or with the help of a technician in the drawing office (Insert your own local name).

                  Comment


                  • #10
                    I don't know if you can get the code for plot, but if you can it might be a good place to start - it may spend time on things you don't need. However, there is the possibility that features of newer Stata versions might help and plot won't be built for them.

                    Comment


                    • #11
                      #10 Phil Bromiley

                      No go, unfortunately:

                      Code:
                      . which plot
                      built-in command:  plot

                      Comment


                      • #12
                        Phil Bromiley one could implement a clone of -plot- with careful application of -display- since I see it has printf style formatting. You could imagine printing a constant sized square matrix of characters such that the width is defined using string formatting and the height is simply the number of calls to -display-. The difficult part would be finding an algorithm to dynamically generate the appropriate -display- arguments for each line of the plot. Backing the plot with a mata matrix data structure isn't strictly necessary (right now I'm tracking the state of the program with an ado class) but that might be the way to go here. Could even implement my own sparse matrix I suppose.

                        But as a practical matter, I haven't run into any problems with -plot- so far, so if I can save myself the work I will. In the mean time I'm still redefining my frame drawing routine for the new implementation, and since I'm new to the language syntax errors are slowing me down.

                        The next challenge on my agenda will ironically be slowing down -plot-, because the thing is too fast to actually see the animation. Probably this will be intrinsically linked to when and how I go about waiting for user input.

                        Comment


                        • #13
                          Speaking of syntax errors, I want to plot a series of points that collectively represent any given frame of the game. To do so, I create a data set with two temporary variables x and y and a number of observations equal to the number of points I need. Then I set x and y for each coordinate.

                          Stata throws the error "in not found" on this line:

                          Code:
                          quietly replace `x' = `.Global.pongstate.leftbound' in 1
                          Where .Global.pongstate.leftbound is a globalized class that represents the current state of the game. .Leftbound is the leftmost "in-bounds" set of x values. Here is the entire draw routine for reference:

                          Code:
                          capture program drop draw_pong
                          program define draw_pong
                              local debug = 1
                              if `debug' display "[DRAW][START] draw pong routine start!"
                              * There are two points on this plot, the ball and the paddle.
                              if `debug' display "[DRAW] setup temporary dataset to represent current state ..."
                              if !(_N == 6) {
                                  clear
                                  quietly set obs 6
                              }
                              * View objects have x and y coordinates.
                              tempvar x
                              quietly gen `x' = .
                              tempvar y
                              quietly gen `y' = .
                              if `debug' display "[DRAW] set up static coordinates ..."
                              * Need to draw four corners to maintain window scale.
                              * top left
                              quietly replace `x' = `.Global.pongstate.leftbound' in 1
                              quietly replace `y' = `.Global.pongstate.upperbound' in 1
                              * top right
                              quietly replace `x' = `.Global.pongstate.rightbound' in 2
                              quietly replace `y' = `.Global.pongstate.upperbound' in 2
                              * bottom left
                              quietly replace `x' = `.Global.pongstate.leftbound' in 3
                              quietly replace `y' = `.Global.pongstate.lowerbound' in 3
                              * bottom right
                              quietly replace `x' = `.Global.pongstate.rightbound' in 4
                              quietly replace `y' = `.Global.pongstate.lowerbound' in 4
                              if `debug' display "[DRAW] set up dynamic coordinates ..."
                              * ball coordinates
                              quietly replace `x' = `.Global.pongstate.ballcurrent.x' in 5
                              quietly replace `y' = `.Global.pongstate.ballcurrent.y' in 5
                              * paddle coordinates
                              quietly replace `x' = `.Global.pongstate.paddlecurrent.x' in 6
                              quietly replace `y' = `.Global.pongstate.paddlecurrent.y' in 6
                              if `debug' display "[DRAW] draw the frame ..."
                              * draw as ascii art.
                              plot `x' `y'
                              if `debug' display "[DRAW][END] draw pong routine end!"
                          end
                          Note that eventually there will be two paddles represented by a list of points rather than a single point. Let's try to keep things simple for now.

                          Any idea what the problem with this syntax is?

                          Comment


                          • #14
                            I do not do class-based programming, so this is something of a guess. The syntax in red in
                            Code:
                            quietly replace `x' = `.Global.pongstate.leftbound' in 1
                            is appropriate for interpolating the value of a local macro named .Global.pongstate.leftbound but I do not believe that object exists as a local macro. So an empty string is interpolated and Stata then sees
                            Code:
                            quietly replace `x' = in 1
                            which is consistent with the error message it gives - you don't have a variable named "in". Try instead
                            Code:
                            quietly replace `x' = .Global.pongstate.leftbound in 1
                            which seems consistent with examples in the documentation.

                            Comment


                            • #15
                              William Lisowski Though this is difficult to discover by reading the documentation, in general to use class variables as values you must use local macro notation. This isn't well documented or even particularly conceptually consistent, but it is the case.

                              On the other hand, I think your point that the local macro is being resolved to the empty string is right on the money. Classes are really just collections of other objects, and I've noticed that the value I'm looking for should be in the constant subclass. Unfortunately, fixing the program results in the same error.

                              Code:
                              quietly replace `x' = .Global.pongstate.leftbound in 1
                              results in ".Global.pongstate.leftbound invalid name"
                              Code:
                              quietly replace `x' = .Global.pongstate.const.leftbound in 1
                              results in ".Global.pongstate.const.leftbound invalid name"
                              Code:
                              quietly replace `x' = `.Global.pongstate.const.leftbound' in 1
                              results in "in not found"

                              However,

                              Code:
                              di `.Global.pongstate.const.leftbound'
                              prints 0 to the console, which is the correct value, and likewise,
                              Code:
                              di `.Global.pongstate.const.rightbound'
                              prints 100 to the console, also the correct value.
                              Last edited by Daniel Schaefer; 11 Mar 2020, 14:37.

                              Comment

                              Working...
                              X