This is a very specific, but I think rather intriguing, programming problem I've come across, and I'm curious how people here would solve it. You can scroll to the end of this post for the basic programming question, but I feel it's necessary to provide a little background and context for how this question arose in the first place.
Background:
A group of my friends likes to play a lot of tabble tennis (ping pong). Recently, on a lark, some of us have decided to start collecting "box scores" on all of our games, and then calculating some analytics on who is best at certain elements of the game, and so forth. Just for fun. As part of this, I've been interested in simulating a table tennis game. That is, given a simplified set of parameters related to a player's performance (e.g. percentage of serves a player aces, the percentage of rallies a player scores a point, the percentage of rallies a player hits it off the table, etc.), I want to be able to simulate 100 games between those two players. Eventually, we want to use this to calcluate a sort of "Wins Above Replacement" stat, where we see how many games a person would be expected to win given their past statistical performance if they are playing an "average" player. (Obviously this is a simplified way of doing things, but we are doing this just for fun!).
Example
For the most part, this simulation is pretty simple to set up in a SAS Data step, using DO and IF loops. For example, the shell of the code might look someting like this:
DATA game;
player1_score= 0;
player2_score= 0;
do until ( (abs(player1_score-player2_score)>=2) & (player1_score>= 11 | player2_score>= 11) );
/* SERVE */
do until (score = 1);
/* DID THEY SERVE OFF THE TABLE? */
player_1_serve_UFE = rand('BERNOULLI',&UFE_rate_serve);
/* If served off table, other player gets point */
if player_1_serve_UFE= 1 then
do;
score = 1;
player2_score= player2_score+1;
end;
else do;
end;
end;
This is a shortened version of the code: this, for example, only evaluates whether or not player 1 serves the ball off the table, and if they do the other player gets the point. The real code is more elaborate, but follows this basic structure, with the DO UNTIL loop being used to satisfy the rules of a table tennis game - play to 11 but have to win by 2, and various IF and DO loops within evaluating our statistics to see whether an event happens and who earns the point. For example, the macro variable &UFE_rate_serve might be the proportion of times a player has served the ball off the table in the past, and is thus being used as an estimator for their error rate. And so on and so forth.
Problem / Question
The one element of the game I am having a difficult time coding is how the service switches in a table tennis game. If you aren't familiar, in table tennis, you serve two points in a row, and then switch; if the score reaches 10-10, you switch serves after every earned point until somebody can win by 2. However, I'm finding that this is frightfully hard to automate in my code.
The part of the program that works well is (like the snippet I gave above) the one that essentially determines what happens after a player serves (and I have similar code for the rally after the serve, all of which works as planned/expected). However, the way the code is currently written, as you can see, is that "player 1" is ALWAYS serving, and player 2 is ALWAYS receiving, which of course doesn't reflect what happens in the game.
So, what I want to be able to do is allocate the serves according to the store. So, I'd use a coin flip to decide which player serves first. Then, after every two earned points, the serve switches. However, I cannot figure out a way to really program this efficiently. I can do something horribly annoying like a really large "select" statement, as follows:
select;
when (player1_score+player2_score<=2)
do;
serve = "player 1"; {code to determine what happens on serve and who scores}
end;
when (2<player1_score+player2_score<=4)
do;
serve = "player 2"; {code to determine what happens on serve and who scores}
end;
(etc.)
But this is a pretty terrible solution, because I basically need to directly enumerate every possible score combination. Further, it is super redundant, because I need to keep repeating the rather complicated series of DO and IF loops within each WHEN statement to actually calculate the score. I know I can replace some of that with a macro to execute, but again I'm not sure if that will even work the way I want it to (after testing similar code a few times it never quite works right).
So, basically, what I want eventually is something along the lines of this:
server = player_1;
do until ( (abs(player1_score-player2_score)>=2) & (player1_score>= 11 | player2_score>= 11) );
(code that switches who the "server" is, every 2 points scored)
do until (score = 1);
(code to determine what happens on serve, etc.);
end;
end;
Does anybody have any idea how to accomplish something like this in SAS? Did I explain my problem clearly enough?
... View more