# How would I predict the best shot in mini-golf ?

I am playing a randomly generated mini-golf game, and I take a turn as a human player. Then my CPU-controlled opponent takes a turn. How does my CPU opponent know at which angle to take a swing?

how would a computer end up at this decision?

It’s something I’m interested in, but I don’t have a good answer yet.

My GUESS is that I would actually simulate a 2D light-source shining out of the ball. Then each wall would be a mirror, and from whichever direction the light hits the hole first would be the best angle.
I think that would work, but how the heck do I actually make that and use it?

What do you think? Is this easier than I think it is?

The “mirror” idea you’re thinking of is called Raycasting. Just use Physics.Raycast or Physics2D.Raycast if it’s a 2D games. Your raycast will hit a collider and return a RaycastHit object, in this RaycastHit object you have details about the hit, like the collider reference, the point on the collider and the “normal” (surface direction" of the hit point on the collider, also calculate the distance it went each raycast and add it to a distance value to limit how far you raycast can travel. Using this surface direction, your initial ray direction and the hit.point, you can trigger another Raycast from that point, in the direction the ray should bounce, there is a helper method for calculating this: Vector3.Reflect. You’ll have to also redo this chain of events for an amount of increments around the ball’s rotation so you can pick and choose which result the CPU should go with, it sounds complicated but it really would only be a few fairly simply methods and a recursive function or two.

So I should raycast outward in a circle, and then also reflect once, twice or three times for each time a ray hits a wall? That’s basically fully simulating every possible shot until you land on the right one.
That’s one solution, but it’s extremely heavy. You’d even need to do more granular checks as the hole gets further away.

I could MAYBE do a calculation like that on the GPU, but I wonder if there’s a cleaner solution that relies more on an algebraic formula than trial-and-error.

One thing I noticed that might help is that in an environment with only right-angles (like the one above) you can at-least rely on your trajectory always being one of 4 angles. There’s a lot of parallel lines, so maybe that’s a clue.

Do a bisection search over each iteration of the result.

That is only considering one variable as well, if it’s a mini golf game shot power is important as well

Why do you need AI, mate just make it muliplayer. Much lazye- SIMPLER. I meant to say simpler.

Nah, it could just cycle through 25% 50% 75% 100%.
If it can’t get a hit like that, what are the odds hitting the hole in one is even possible?

@TomBrien The colliders you’re going to be raycasting against have very few vertices, you could do a few hundred casts in a frame on fairly low performance hardware. And as Marfab mentioned, you can do a refinement search, so you do maybe 12 directions around yourself, then do a further amount of casts between the two angles that bring you closest.

Here’s an example of me doing 360 raycasts around my object per frame, with no bounce limit, only a 25 unit distance limit, a ray becomes more red by 10% each hit.
https://gfycat.com/FancyBronzeFlatcoatretriever

In the tightest area of that clip where they all bunch up, I was reaching 2000+ raycast hits and still only average around 1ms, and that’s on top of drawing all these debug rays and profiler running and in editor…

People seem to have this idea that raycasts themselves are expensive, I guess perhaps because of the perception about how they’re used for photorealism, but in reality raycasts are quite efficient, so long as you keep the geometry you’re casting against simple. We’ve been using raycasts in games since the 8-bit era.

Also it should be mentioned… It’s not like you have to do this every frame. This is a one-time operation each time your NPC needs to putt. You could even do it in an asynchronous method and wait for result if you don’t want people to notice a potential stutter on the frame it calculates. Or use system timer to spread the calculation over multiple updates manually.

Here’s the test if anyone wants to play with it:

``````//RecursiveRaycast test by Invertex
using UnityEngine;
public class RecursiveRay : MonoBehaviour {
[SerializeField]float maxRayTravel = 20f;
RaycastHit hit;

void Update(){
Vector3 rot = transform.localEulerAngles;
for(int i = 0; i < 360; i++){
transform.localEulerAngles = new Vector3(rot.x, (float)i, rot.z);
RaycastRecursion(transform.position, transform.forward, maxRayTravel, new Color(1f,1f,1f));
}
transform.localEulerAngles = rot;
}

void RaycastRecursion(Vector3 position, Vector3 dir, float maxDist, Color col){
if(Physics.Raycast(position, dir, out hit, maxDist, layerMask))
{
float dist = Vector3.Distance(position, hit.point);
Debug.DrawRay(position, dir.normalized * dist, col);
maxDist -= dist;
if(maxDist > 0) {
col.g -= 0.25f; col.b -= 0.25f;
RaycastRecursion(hit.point, Vector3.Reflect(dir, hit.normal), maxDist, col);
}
}
else{ //Disabled this part if you only want to see rays that have hit something
Debug.DrawRay(position, dir.normalized * maxDist, col);
}
}
}
``````
1 Like

Okay thanks. You’re right, apparently I shouldn’t be so scared of blasting out rays in every direction.
It felt like a brute-force solution, but I guess that’s what people do.

Very cool demo aswel.

Realized my radial distribution wasn’t even for some reason, updated the code
https://gfycat.com/DisguisedIdenticalAfricanaugurbuzzard

1 Like

That is an awesome demo you set up. I have no use case for this currently, but I can see this coming in handy some day down the road, so I ended up bookmarking this thread. Thanks! Watching that demo, I feel like I just tried LSD for the first time! :lol: Seriously though, excellent demonstration of your method.

1 Like

This. Take a look at Peggle for example. It pops up a “Calculating best shot…” label when using the Zen option, and there’s a small delay. I’m pretty sure the amount of variables in that game are far more complex than a mini golf game.

(Unless you had a golf course as complex as a Peggle level )

It’s quite easy to do the same for an AI controller golfer where the golfer can look at the hole then back at their ball, take a couple of practice swings, etc.

You can always try to reduce some possibilities.
Also if you know a power/speed you can stop it early. Only keep casting if your ball still has enough energy.

There would be some rate of speed loss over the distance traveled. Friction. Each collision would reduce the energy even more. To at some point you could probably stop. But if you have slopes it gets more complicated.

Depending on your simulation you can just run a few versions of it and let the CPU pick the best one. It doesn’t have to be the perfect one. A golf game I worked on before simulated everything constantly to predict where the ball would go. Then it would simulate one last time on the shot and the ball moving was just playing that simulation result. It want actually simulating as it was rendering.

you could go about it in steps. if you would add tags for obstacles and walls

1. raycast from ball to hole, keeping the positions of obstacles.
2. raycast from obstacle position to wall (shoot 2 or 4 rays to filter out the right wall) keep the hit point on the wall as the initiale positions then shoot a Ray and reflect from the first wall position to the second wall position, reflect there to see if you hit the hole if not slightly move the positions until you hit the hole*.
*3) you could set up something to see how far or just how (overshoot/undershoot) you missed the hole to limit the iterations
once you hit it you have all the data you need to shoot.