BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
RyanSimmons
Pyrite | Level 9

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?

 

1 ACCEPTED SOLUTION

Accepted Solutions
Rick_SAS
SAS Super FREQ

I'd write the code in terms of players 0 and 1 and use arrays to hold scores and other stats.

Every time you want to switch servers, assign

server = mod(server+1, 2);

receiver = mod(server+1, 2);

I think this will prevent dupkication of code; all code will be in terms of the server and receiver, rather than player1 and player2.

View solution in original post

2 REPLIES 2
Rick_SAS
SAS Super FREQ

I'd write the code in terms of players 0 and 1 and use arrays to hold scores and other stats.

Every time you want to switch servers, assign

server = mod(server+1, 2);

receiver = mod(server+1, 2);

I think this will prevent dupkication of code; all code will be in terms of the server and receiver, rather than player1 and player2.

RyanSimmons
Pyrite | Level 9

I accepted this as my solution because the use of the "mod" function in that way was helpful, even if I feel it didn't get at the real heart of my question. 

 

My full solution ended up looking like this:

if team1_score = 10 & team2_score = 10 then overtime = 1; 
				else overtime = 0;

			select;
				when (overtime = 0 &  mod(team1_score+team2_score,2)=0)
					do;
		   				team1_serve = mod(team1_serve+1,2);
						team2_serve = mod(team2_serve+1,2);
						team1_receive = 1-team1_serve;
						team2_receive = 1-team2_serve;                  
					end;
				when (overtime = 1)
					do;
						team1_serve = mod(team1_serve+1,2);
						team2_serve = mod(team2_serve+1,2);
						team1_receive = 1-team1_serve;
						team2_receive = 1-team2_serve;  
					end;
				otherwise;
			end;

team1_score = team1_score + team1_serve*server_score + team1_receive*receiver_score;
			team2_score = team2_score + team2_serve*server_score + team2_receive*receiver_score;

This was the only way I could really get the simulation to work as expected. I couldn't quite figure out a solution using arrays due to the number of conditionals I had to deal with, though perhaps a more clever programmer would. In any case, this code does work as intended.

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 2 replies
  • 927 views
  • 0 likes
  • 2 in conversation