Thursday, February 28, 2013

Soccer Analytics

I'm back.  Honestly I forgot I had this blog.  I would say everybody else forgot too but that would require others to know it existed in the first place.  I suppose this is more of a journal if I'm the only one that reads it but... oh well.

On to the topic of the day.  Soccer Analytics.

I'm psyched to try my hand at some spatial sports analysis.  The MIT sports analytics conference is in town and although I'm not going it's rekindled my desire to do some data analysis.

The idea.

Compare player actions on the field with some idealized sim.  So basically write some soccer AI and then feed it some sample game state and see what choice your AI would make vs what choice the real live player made. 

I'm actually all ready to try this out as I have a c++ AI framework and a fancy C# data visualizer app.  See below


So in the app I would load up one second of the gameplay.  In the console I would type run

    insert (@(../)) (#(run simulation_soccer ($(@(./)))))

where the commands are:
  • insert - insert item into knowledge database at some location
  • @ - this grabs key value pair at this location
  • # - deserialize data into key value pair
  • run - execute external .exe
  • $ - serialize object to text
So the command above does the following
  1. grabs the current key value pair that is selected
  2. serializes that item to text
  3. executes external .exe simulation_soccer with text data as argument
  4. deserializes that data back to a key value pair
  5. inserts that data into the parent of the current node
Hopefully you can see the workflow here.  I'm hoping to fancy this up to use in video game AI but I think it will work as a general purpose AI simulator/visualizer. 

So what I need is some sample data to start writing simulation_soccer.exe.  This will obviously be extremely difficult but there are some things that make it not so terrible.  For one I don't really have to do everything all at once.  I could do simulate_soccer_left_back_on_break_aways.exe or some other reduction of the problem.  I also already have an AI framework that may or may not be fast enough to use in real time games but will definitely work for this.

AI Framework

Basically it's the above knowledge format with save/load etc + libraries for querying, searching and even planning.  You might think to yourself why planning?  Well because planning is awesome and how better to evaluate actions than to consider what might have been?  Passing left or right at a particular instant might seem arbitrary but if the next pass on the left is possibly across the face of goal while right is back to the fullback well then the value becomes more clear.

So how does the API look you might ask.  Well I'll try to show you.

BOOST_AUTO_TEST_CASE( walk_10m )
{
 allocator::stack<20*KILOBYTES> a;
 model_test::model_factory f(a);
 model_test::model_locomotion m(a);
 model_test::condition_distance condition(a);
 model_test::heuristic_distance heuristic(a);

 aisearch_astar::search::algorithm alg(a, condition, heuristic);
 aisearch_astar::search::state s(a);
 aisearch_astar::search::context context(s);
 aisearch::search::result result(a);
 aisearch::vm::machine machine;

 aiplan::search::monitor monitor(a, m, f);
 aiplan::graph::edge e1;
 aiplan::graph::vertex v1(a);
 aiplan::model::op_noop noop;
 aiplan::model::state start = f.create_state();
 start.store(aiplan_test::pose_key, aiplan_test::standing);
 start.store(aiplan_test::position_key, aiquery::float3(0.0f, 0.0f, 0.0f));
 start.store(aiplan_test::target_key, aiquery::float3(10.0f, 0.0f, 0.0f));
 start.store(aiplan_test::forward_key, aiquery::float3(1.0f, 0.0f, 0.0f));

 v1.setstate(start);
 e1.setoperator(noop);
 e1.setvertex(v1);

 machine.start(monitor, context, alg, e1);
 while( machine.step(monitor, context, alg) == aisearch::vm::machine::kprocessing )
 {
 }
 machine.stop(monitor, context, alg);
}


The above code is the unit test I use for testing animation planning for walking 10m.  There is a ton missing but hopefully it makes some tiny bit of sense to the c++ devs out there.  Basically it's just a bunch of declarations followed by the creation of the start state followed by the while loop that searches to find the plan. 

The interesting bit here is the model that is planned over.  model_locomtion does 2 things.  It selects the operators for the plan when a node is expanded and it scores the operators for sorting the fringe of the the search.

  virtual void select(aiplan::model::model::operators& o_operators, const aiplan::model::state& in_state) const
  {
   o_operators.push_back(&op_walkstart);
   o_operators.push_back(&op_walkforward);
   o_operators.push_back(&op_walkstop);
  }

  virtual float score(const aiplan::model::op& in_operator, const aiplan::model::state& in_prestate, const aiplan::model::state& in_posttate) const
  {
   allocator::stack<2*KILOBYTES> a;
   aiquery::state s(a);
   aiquery::context c(s);
   aiquery::binding b;
   aiquery::predicate1<aiquery::float3> position(s, aiplan_test::position_key);
   aiquery::predicate1<aiquery::float3> target(s, aiplan_test::target_key);
   aiquery::value<float> value = aiquery::distance(position(b), target(b));
   b.bind(in_prestate);
   float distance;
   value.evaluate(distance, c);

   if ( &in_operator == &op_walkstop
    && distance > 1.0f )
   {
    return distance;
   }


   return 0.0f;

  }

So how will I use this for generating soccer simulations and analyzing performance.  Well step one is to create the operators.  I think this is actually a really hard step because coming up with a good set of planning operators will determine whether this methodology is worth a shit.  Step two is to come up with a way to score the operators based on the coaches model.  Step 3 is to compare the output of the model vs the next second/data step of gameplay.

Thats the master plan.

No comments:

Post a Comment