billiards-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Billiards-devel] Programming experiments with Billiards


From: Dimitris Papavasiliou
Subject: Re: [Billiards-devel] Programming experiments with Billiards
Date: Thu, 14 Jan 2010 12:15:56 +0200

Hello Tadej,

what you want to do makes sense and should be possible relatively easily.  In fact, it was what Billiards was designed for.  There will be some scripting involved though.  How much Lua do you know?

The initial setting you describe would be a bit of a problem right now.  It's fairly easily done by hacking the internals of Billiards but not by just *adding* some code as it should be done.  During the development of  Billiards I needed to perform some experimentation in order to determine whether the physics simulator actually produced the correct results (to my surprise, it actually did without requiring any special tweaking).  So I created an experimentation mode which featured only one white ball on a Carom table.  You could then easily perform squirt experiments etc.  That's all I needed back then but it seems now that I'll have to expand experimentation mode so that you can somehow describe which balls you want at startup and where you want them placed.  I should be able to make the necessary changes soon so I'll send you a patch and instructions on how to do what you want then.  In the meantime, let's focus on the rest.  (You can start Billiards in experimentaion mode btw with the -Oexperiment flag).

Now regarding the second point: sidespin, folllow and elevation should be easy to setup but "shot force" is not really.  The problem is that it's difficult to even define what this "shot force" would be.  During the short time that collision between the cue tip and ball takes place a force is exerted on the ball by the tip but as all impact forces it's hard to determine what it is and it's not constant anyway.  This is in the "simple" case where the tip or ball does not flex, which of course does happen.  So in short you can apply a force to the ball at a specific offset and elevation instead of actually whacking it with the cue but this would probably not produce the desired results.  If however all you need is to be able to perform a set of shots with the same force or with "more" or "less" force there are things you can do.  Let's take it from the top.  I'll be as brief as possible assuming you only want to know as much as absolutely necessary to be able to implement what you describe.  If you need more detail let me know.  I'll be happy to provide it as I need to write a manual on how to program Billiards and Techne at some point anyway.

Billiards uses the concept of hooks to perform certain actions at certain events.  Events can be, for example the fact that the player lines up a shot (starts aiming), takes a shot when two balls collide or when a ball bumps agains the cushions etc.  When these happen certain functions are called to perform some tasks.  These functions can be assinged by Billiards (that is by me)  or by the user (and that would be you).  For example when you click on the cueball to start aiming all functions associated with the "aiming" hook are called and amongst them the following which you can find inside game.lua which should reside in something like /usr/local/share/techne/billiards:

billiards.aiming.reset = function ()
   graph.transition = transitions.dissolve {
      duration = 0.7
   }

   bodies.cue.elevation = 0
   bodies.cue.sidespin = 0
   bodies.cue.follow = 0

   bodies.observer.longitude = bodies.cueball.position[1]
   bodies.observer.latitude = bodies.cueball.position[2]
   bodies.observer.radius = 0.4
   bodies.observer.elevation = math.pi / 2 -
                   bodies.cue.elevation -
                   math.rad (5)
end

What this does is fade the screen to provide a nice transition, reset the elevation, and offsets to zero degrees (or was that radians) and meters respectively and to setup the observer (that is the "camera") how it should be (or how I like it if you prefer). Now if you want to change some of this, presumably the shot parameters the best way to go about it would be this:

local oldreset

oldreset = billiards.aiming.reset

billiards.aiming.reset = function ()
   -- First call the old function to setup the default
   -- behavior we don't want to change.

   oldreset()

   -- Now change what we don't like.

   bodies.cue.elevation = math.pi/4
   bodies.cue.sidespin = billiards.ballradius * 0.3
   bodies.cue.follow = -0.003
     end

You may be wondering where to put that code but we'll get to that later.  Now setting up the shot is another matter.  The whole world in Billiards (actually as small as a room with no walls and a pool table) is set up of nodes connected in a graph, sort of like a tree.  The whole graph is traversed (several times actually per frame) and certain actions are performed by each node according to its nature.  For example a node representing a physical body might perform collision detection and physical simulation for this frame, a node representing a visible surface (of a ball conveniently enough) might draw itself on the screen.  Now there also happens to be a node representing a "slider" that is a joint between two bodies which allows them to slide against each other along a certain axis.  This node actually links the cue stick with your hand (which is not drawn) and can also be powered, as if by a motor, which in the real world would be your right arm.  So this joint is called joints.arm and the parameters of its motor are set like so:

joints.arm.motor = {v, F_max}

What this actually means is that you want the motor to power the bodies it's linked to in such a way that their relative velocity along the joint's axis becomes v and in order to do this it can exert a force on the bodies no larger than F_max.  In our case, since the hand is considered fixed, this means that your right arm can push the stick with no more than F_max newtons of force in order for the tip to reach and maintain a velocity of v m/s.  The stick needs to be accelerated to that velocity though so the larger the force the sooner it will speed up to v.  So if you use the same motor parameters and the same initial position for the stick relative to the ball (this latter comes into play because, if F_max is too low the stick might hit the ball before it has the time to reach the target velocity), if therefore both these conditions hold then the stick will contact the ball with the same velocity and provide identical (more or less, hopefully more more than less) results between shots.  Raising v and making F_max large enough to reach it should result in more energy being transferred between the balls and cue.  Incidentally there's a hook called billiards.cuecollision I believe which calls its functions right before the cue collides with a ball.  You can use that to measure the velocity of the cue stick at that moment to make sure it is what you want it to be.  The cue is described by the node bodies.cue and its velocity can be read at bodies.cue.velocity so you can write:

billiards.cuecollision.cuespeed = function()
    print (math.length (bodies.cue.velocity))
end

So what you would presumably want would be to be able to hit a key and have the stick launch to reach a certain speed before hitting the ball.  You need to bind some code to a key and you can use a node to do that.  There's a node called an event node.  When this is traversed it queries the windowing system for input events and fires hooks for each one.  So you need to define such a node and link it to the tree somewhere (where exactly is not important as long as you don't, by chance, overwrite one of my nodes).  Do it like this:

graph.launcher = frames.event {
   keypress = function (self, key)
      if key == 'f' then
            -- Substitute suitable values here.
            joints.arm.motor = {v, F_max}
      end
   end
}

Moving on to the third point.  You only want the final positions so you need to measure them once the shot is over.  There is no "finished" event and hook but there are "waiting" and "looking" events.  (You start waiting as soon as you strike the cue ball and when these come to a rest you start looking once more to decide upon your next shot).  You could use hooks for both these events to detect a transition between them or alternatively I could introduce a "finished" event which would be as simple.  But I suppose the best way to go would be to bind the logging to a key so that you have more control over it.  You can consult the lua manual to find out how to open a file and write to it.  I'll just provide some sample code to print it on the terminal:

graph.logger = frames.event {
   keypress = function (self, key)
      if key == 'l' then
         for i, ball in bodies.balls do
            print (string.format ("%d: {%f, %f, %f}\n", i,
                                  ball.position[1],
                                  ball.position[2],
                                  ball.position[3]));
         end
      end
   end
}

As you can see bodies.balls is actually a table with all the ball nodes inside.  You traverse this and for each ball node you print it position vector.  Simple enough I believe.  After the shot is over and logged you need to reset the balls to their initial positions probably and this can be done by setting their postion in much the same way as you printed it:

         bodies.balls[1].position = {x_w, y_w, billiards.ballradius}
         bodies.balls[2].position = {x_b, y_b, billiards.ballradius}

You can put this code right after the logging takes place if you want.  This is more or less it for now.  One thing that remains to be mentioned is where to put all this code.  You can actually specify additional script files for Billiards (or rather Techne) to run with the -c switch.  So put all your code in a script file called, say experiment.lua and put this in say ~/foo.  You can then run it like this:

billiards -c ~/foo/experiment.lua

I should also say that all the code above has either been copied from Billiards' source or written on-the-fly and hasn't been  tested at all, as I can't run Billiards at work.  It might therefore not work as advertised and probably won't as I haven't done anything to Billiards for a while.  It should get you started nevertheless.  I advise you to look through Billiards' source as well.  Most of it will probably not make much sense but you should get a general idea of how it works and it's not that much anyway.  I'll try to implement my suggestions myself when I make the changes I mentioned earlier but this might take a few days.  Give it a shot on your own until then and let me know if you need further clarifications, explanations, help or whatever.

Dimitris


2010/1/11 Tadej Janež <address@hidden>
Hi!

I've come across your Billiards simulator on The Linux Game Tome,
installed it, played with it for a while and I really like it so far.

Now on to the thing I would like to achieve with Billiards.

Firstly, I would like to specify a particular initial setting:
Table with pockets, white ball at position (x_w, y_w), black ball at
position (x_b, y_b) and no other balls.

Secondly, I would like to try many different shots, i.e. set different
parameters for Sidespin, Follow, Elevation and "Shot Force". However,
instead of using the graphical interface, I would like to specify these
parameters programmatically.

Thirdly, I would like to log/capture the final coordinates of both white
and the black ball.

All in all, I would like to write a script, which would simulate many
different shots from the given initial position and log the results to a
file.

If none of the above makes sense, please say it and I'll try to explain
it better.

Can you get me some tips to get started?

Thanks,
Tadej

--
Tadej Janež <address@hidden>
Fakulteta za računalništvo in informatiko





reply via email to

[Prev in Thread] Current Thread [Next in Thread]