I'm making a PONG game for a school project using Kivy in Python. So far thanks to this forum I've made myself some AI for the NPC paddle.
This is the code:
if self.ball.y < self.player2.center_y:
self.player2.center_y = self.player2.center_y - 4
if self.ball.y > self.player2.center_y:
self.player2.center_y = self.player2.center_y + 4
This is in a method of PongGame() class called ArtificialIntelligence().
I use this to call it:
Clock.schedule_interval(game.ArtificialIntelligence, 1/300)
This allows me to call it once every 1/300th of a second. However, anything more than 1/300, I seem to have no difference. I.e. 1/9001 does not call it once every 1/9001th of a second.
The way it works is that it increases the y coordinate 4 pixels relative to the balls position, and it does this once every 1/300th of a second, hence why it doesn't "lag" at this rate. This is basically an "easy" mode for the player. If I want to do a "hard" mode, I need to make the NPC more accurate. I can do this by doing
self.player2.center_y = self.player2.center_y + 20
Something like this. This would be extremely accurate. HOWEVER, it does NOT look "fluid", it looks "laggy". I assume I could get the same amount of movement by calling the method more often instead of changing the amount it moves via altering the pixel movement. However, I don't know how to do this, because, as I said, changing it from anywhere above 1/300 seems to make no difference.
This is how I use my paddle:
if touch.x < self.width/3:
self.player1.center_y = touch.y
and I can move it as fast as I want because this updates as I move the mouse. And it looks fluid because it updates as often as it needs to update. I don't know how to do this with my AI.
Does anyone know how I could basically make the NPC paddle more accurate, allowing me to do Easy-Normal-Hard, etc, while retaining fluidity and no lag? I see only one way I could do it: Increase the amount the method is called.
However I bet there is a better way and I don't know how to do it. Does anyone have any Idea how I could do this? Thanks.
Edit: it looks like I can do it like this:
Clock.schedule_interval(game.ArtificialIntelligence, 1/300)
Clock.schedule_interval(game.ArtificialIntelligence, 1/300)
Clock.schedule_interval(game.ArtificialIntelligence, 1/300)
Clock.schedule_interval(game.ArtificialIntelligence, 1/300)
Clock.schedule_interval(game.ArtificialIntelligence, 1/300)
But that seems REALLY ugly and REALLY inefficient... I'd MUCH prefer a cleaner way.
At 300 frames per second, the problem is not in the rate of updates because you are exceeding the human eye's capacity to perceive movement by a factor of 50 or more.
The jerky movement comes because the ball is following a linear trajectory while your paddle is just hopping to where the ball is now. Ideally, your computer player could compute where the ball will be when it hits the plane of the computer paddle and then take a very smooth course to that location at 30 frames per second or less. Sure, the prediction math requires a tiny amount of trigonometry but it is the "right" way to do it in the sense of that is how a good player would play, by anticipating.
It would be far easier to just increase the size of the computer's paddle which would also give a visual indication to the human player of just how much harder the game is. When the computer's paddle has become a wall, the player would see that there is no winning to be done. The larger paddle would have the side effect of being less jerky, but whether this is a "good" way is your decision.
My advice is to use trig to work out where the ball will be, and have the paddle move there to intercept it.
Animation will be smooth at 30 frames per second.
When making a game AI is quite important these days that the player does not see it 'cheating' and giving it a larger paddle, or the ability to teleport, would be obvious signs of this. It should play in the same manner as a human, just better - not using some mechanism the player has no access to. "This game sucks because the CPU cheats" is a very common negative comment on videogame forums.
So if you need the computer to miss, make sure its trig calculations are off by a random factor, so the player cna't distinguish its play from a human's.
edit: For example: If random (X) <= speed of ball, then intercept correctly, otherwise miss by random (Y) units.
Thanks for your help guys, I was able to work it out with my teacher based on your help and his.
He developed this algorithm (tbh he did it so fast that I wasn't really able to comprehend it!) but this essentially uses trig to generate where the paddle will go (Note, I don't use the angle as I have other values that can be used)
def AIController(self, *args):
ballsFuturePos = self.ball.center_y + ((self.width - self.ball.center_x) / self.ball.velocity_x) * self.ball.velocity_y
numIterations = ((self.width - self.ball.center_x) / self.ball.velocity_x)
#print ballsFuturePos
#print numIterations
if numIterations > 0:
self.wantedPos = self.player2.center_y +(ballsFuturePos - self.player2.center_y) / numIterations
#print wantedPos
#self.player2.center_y = wantedPos + (error / wantedPos) * 100
if self.player2.center_y < self.wantedPos:
self.player2.center_y = self.player2.center_y + 9
if self.player2.center_y > self.wantedPos:
self.player2.center_y = self.player2.center_y - 9
So I generate where the ball is going to hit the rightmost part of the screen by getting the balls y position, adding the (width - the x position of the ball) (which gives me how far until the rightmost part of the screen is in x pixels), then I didivde that by the x velocity, and times the whole thing by the y velocity, which gives me the slope (I think), and as such now that the slope is calculated it also means It has the trajectory and as such can predict where the ball will hit the screen.
I calculate the number of iterations needed to get to the ball by taking the width of screen and minusing it by the balls x position, which calculates how far it is until the rightmost part of the screen, and divide that by the velocity. This gives me a number that I can use to iterate.
now I iterate with this, and create a variable called wantedPos which is where I want my paddle to go. This uses the paddles y position, adding (where the ball will be - where the paddle is), which gives me the distance between the ball and the paddle, divided by the number of iterations, which gives me the position the paddle will be to be at the same position at the ball. As numIterations decreases in each call, so does the wantedPos, meaning the gap gets smaller. I then iterate the paddle closer to the ball, using 9 as a speed, which allows me to increase or decrease difficulty.
Thanks. If I messed up any of my logic in trying to describe his actions, please tell! I think I understand it but a confirmation would be nice :)
Related
I have a physics engine that moves objects like this:
speed = 10
while game_running == True:
speed *= 0.9
position += speed
I want to calculate the amount of speed required to move an object x pixels. I can't figure out how, aside from brute force. Is there any way to efficiently calculate the speed required to move objects? (To move an object you set the speed variable)
I'm trying to code my Pygame game so the player falls anytime it's not touching the floor. The problem is that the way to code it to test if the player y coordinate is lower that 0, and then stop the falling process, but cuadratic function is not always going to get to the exact y = 0, since it travels more pixel each frame, maybe the first value that is lower than 0 is -10. And then the player ends up under the screen.
I thought about testing if the player's y coordinate is lower that 0, and then teleporting him to y = 0 but that feels like a really non elegant solution and it also makes the player teleport for a frame.
"I thought about testing if the player's y coordinate is lower that 0, and then teleporting him to y = 0, but that feels like a really non elegant solution"
This is the usual way. The collision stops the player from falling.
"it also makes the player teleport for a frame."
No it doesn't. You have to "teleport" the player plyer after the collision detection and before you draw it.
You do the collision detection once in every frame. Once a collision is detected, you know that the player has hit an object at some point while moving since the last frame. Instead of calculating the exact time when the player hits the object and stopping movement, you limit the player's position by the object.
i have this simple game where there is a ball bouncing on the screen and the player can move left and right of the screen and shoot an arrow up to pop the ball, every time the player hits a ball, the ball bursts and splits into two smaller balls until they reach a minimum size and disappear.
I am trying to solve this game with a genetic algorithm based on the python neat library and on this tutorial on flappy bird https://www.youtube.com/watch?v=MMxFDaIOHsE&list=PLzMcBGfZo4-lwGZWXz5Qgta_YNX3_vLS2, so I have a configuration file in which I have to specify how many input nodes must be in the network, I had thought to give as input the player's x coordinate, the distance between the player's x-coordinate and the ball's x-coordinate and the distance between the player's y-coordinate and the ball's y-coordinate.
My problem is that at the beginning of the game I have only one ball but after a few moves I could have more balls in the screen so I should have a greater number of input nodes,the more balls there are on the screen the more input coordinates I have to provide to the network.
So how to set the number of input nodes in a variable way?
config-feedforward.txt file
"""
# network parameters
num_hidden = 0
num_inputs = 3 #this needs to be variable
num_outputs = 3
"""
python file
for index,player in enumerate(game.players):
balls_array_x = []
balls_array_y = []
for ball in game.balls:
balls_array_x.append(ball.x)
balls_array_x.append(ball.y)
output = np.argmax(nets[index].activate(("there may be a number of variable arguments here")))
#other...
final code
for index,player in enumerate(game.players):
balls_array_x = []
balls_array_y = []
for ball in game.balls:
balls_array_x.append(ball.x)
balls_array_y.append(ball.y)
distance_list = []
player_x = player.x
player_y = player.y
i = 0
while i < len(balls_array_x):
dist = math.sqrt((balls_array_x[i] - player_x) ** 2 + (balls_array_y[i] - player_y) ** 2)
distance_list.append(dist)
i+=1
i = 0
if len(distance_list) > 0:
nearest_ball = min(distance_list)
output = np.argmax(nets[index].activate((player.x,player.y,nearest_ball)))
This is a good question and as far as I can tell from a quick Google search hasn't been addressed for simple ML algorithms like NEAT.
Traditionally resizing methods of Deep NN (padding, cropping, RNNs, middle-layers, etc) can obviously not be applied here since NEAT explicitly encodes each single neuron and connection.
I am also not aware of any general method/trick to make the input size mutable for the traditional NEAT algorithm and frankly don't think there is one. Though I can think of a couple of changes to the algorithm that would make this possible, but that's of no help to you I suppose.
In my opinion you therefore have 3 options:
You increase the input size to the maximum number of balls the algorithm should track and set the x-diff/y-diff value of non-existent balls to an otherwise impossible number (e.g. -1). If balls come into existence you actually set the values for those x-diff/y-diff input neurons and set them to -1 again when they are gone. Then you let NEAT figure it out. Also worth thinking about concatenating 2 separate NEAT NNs, with the first NN having 2 inputs, 1 output and the second NN having 1 (player pos) + x (max number of balls) inputs and 2 outputs (left, right). The first NN produces an output for each ball position (and is identical for each ball) and the second NN takes the first NNs output and turns it into an action. Also: The maximum number of balls doesn't have to be the maximum number of displayable balls, but can also be limited to 10 and only considering the 10 closest balls.
You only consider 1 ball for each action side (making your input 1 + 2*2). This could be the consideration of the lowest ball on each side or the closest ball on each side. Such preprocessing can make such simple NN tasks however quite easy to solve. Maybe you can add inertia into your test environment and thereby add a non-linearity that makes it not so straightforward to always teleport/hurry to the lowest ball.
You input the whole observation space into NEAT (or a uniformly downsampled fraction), e.g. the whole game at whatever resolution is lowest but still sensible. I know that this observation space is huge, but NEAT works quite well in handling such spaces.
I know that this is not the variable input size option of NEAT that you might have hoped for, but I don't know about any such general option/trick without changing the underlying NEAT algorithm significantly.
However, I am very happy to be corrected if someone knows a better option!
Hello guys I am making a game in python tkinter and where you control a character that is able to shoot where you click. I want the bullet to move towards the point player clicked and beyond always with the same speed using move and after methods and I don't have the knowledge of math to think about an equation that calculates the x and y distances needed to move the bullet towards the point player clicked and beyond till it reaches end of the canvas. Any ideas? thanks for your time and answer
I already have the player and tkinter class and the method that creates the bullet on click and pass it click coordinates. Now I need to calculate teh change of x and y after one refresh
def fly(self):
self.app.canvsas.move(self.bullet, x and y you calculated)
self.app.canvas.after(some refresh time, self.fly)
You probably want to give the bullet a x_speed and y_speed when it is fired and then update each frame by moving the specified amount. Lets say you click at (cx,cy) with the bullet's start location being (sx,sy). From here you'll want to get the angle made to find cos(angle) and sin(angle). Luckily we can get these without even getting the angle! With the direct distance from click point and start point r = ((sx-cx)**2+(sy-cy)**2)**0.5 we will have:
cos_angle = (cx-sx)/r
sin_angle = (cy-sy)/r
Which can be saved in your bullet instance and multiplied by your bullet's speed to get how far you want to move said bullet in the x and y direction:
def updatebulletbycallingthiseveryframe(self):
self.x += self.bullet_speed*self.cos_angle
self.y += self.bullet_speed*self.sin_angle
This will keep the bullet moving forever, add a check to see if it is out of bounds and delete the bullet if that happens.
This is my first question ever, and I am a complete and utter beginner, so please don't eat me :) What I am trying to to is to draw a fibonacci sequence using the Python turtle module. My code is as follows:
import turtle
zuf = turtle.Turtle()
while True:
zuf.forward(10)
zuf.left(3.1415)
This, however, drives around in circles only. I have tried to create a variable, say X, and assign a fibonacci rule to it xn = xn-1 + xn-2 then I'd put it in here zuf.forward(x) but it doesn't work. I tried multiple variations of that, but none seems to work. Please don't give a whole solution, only some hint, thanks a lot.
I think I can get you from where you are to where you want to be. First, your invocation of:
zuf.left(3.1415)
seems to indicate you're thinking in radians, which is fine. But you need to tell your turtle that:
zuf = turtle.Turtle()
zuf.radians()
this will still make your code go in circles, but very different circles. Next, we want to replace 10 with our fibonacci value. Before the while loop, initialize your fibonacci counters:
previous, current = 0, 1
as the last statement in the while loop, bump them up:
previous, current = current, current + previous
and in your forward() call, replace 10 with current. Next, we need to turn the line that it's drawing into a square. To do this, we need to do two things. First, loop the drawing code four times:
for i in range(4):
zuf.forward(current)
zuf.left(3.1415)
And second, replace your angle with pi/2 instead:
zuf.left(3.1415 / 2)
If you assemble this all correctly, you should end up with a figure like:
showing the increasing size of the fibonacci values. Not the greatest looking image, you'll still have to do some work on it to clean it up to look nice.
Finally, I was impressed with the fibonacci drawing code that #IvanS95 linked to in his comment, that I wrote a high speed version of it that uses stamping instead of drawing:
from turtle import Screen, Turtle
SCALE = 5
CURSOR_SIZE = 20
square = Turtle('square', visible=False)
square.fillcolor('white')
square.speed('fastest')
square.right(90)
square.penup()
previous_scaled, previous, current = 0, 0, 1
for _ in range(10):
current_scaled = current * SCALE
square.forward(current_scaled/2 + previous_scaled/2)
square.shapesize(current_scaled / CURSOR_SIZE)
square.left(90)
square.forward(current_scaled/2 - previous_scaled/2)
square.stamp()
previous_scaled, previous, current = current_scaled, current, current + previous
screen = Screen()
screen.exitonclick()
This is not a whole solution for you, only a hint of what can be done as you're drawing your squares and this is a stamp-based solution which plays by different rules.