Python Kivy how do I press multiple keys at once? - kivy

I am making a simple game in the coding language python in the module Kivy, where a guy moves across and around the screen using "wasd" commands. The problem I have ran into is not being able to press multiple keys at the same time. For example, if the player wants to press "w" and "d" at the same time to go diagonal in the upper right, they can't. It only lets them go one direction at a time. Below is some of my code.
class MainScreen(RelativeLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.guy_size = dp(50)
self.velocity = 3
self.acceleration = 0.5
with self.canvas:
Color(1, 0, 0)
self.guy = Ellipse(pos=(100, 400), size=(self.guy_size, self.guy_size))
self._keyboard = Window.request_keyboard(self.keyboard_closed, self)
self._keyboard.bind(on_key_down=self.on_keyboard_down)
self._keyboard.bind(on_key_up=self.on_keyboard_up)
Clock.schedule_interval(partial(self.move_guy, 'nothing'), 1/60)
def keyboard_closed(self):
self._keyboard.unbind(on_key_down=self.on_keyboard_down)
self._keyboard.unbind(on_key_up=self.on_keyboard_down)
self._keyboard = None
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
global RUNNING
if keycode[1] == 'w':
self.move_guy('w')
if keycode[1] == 's':
self.move_guy('s')
if keycode[1] == 'a':
self.move_guy('a')
if keycode[1] == 'd':
self.move_guy('d')
if keycode[1] == 'w' and keycode[1] == 'd':
self.move_guy('wd')
return True
def on_keyboard_up(self, keyboard, keycode):
pass
def move_guy(self, direction, *largs):
x, y = self.guy.pos
if direction == 'w':
y += 5
if direction == 's':
y -= 5
if direction == 'a':
x -= 5
if direction == 'd':
x += 5
self.guy.pos = (x, y)

Related

How do I train a model for an environment with variable features using openai gym?

I have a grid network (like a maze) and I want to find the shortest route from the source node to the destination node.
I've coded that and it works well. It finds the shortest route for a network of dimensions x*y and with the specific nodes as the source and destination nodes which are determined as inputs to the environment. env = netEnv(3, 4, 1, 8) means my env is a 3*4 grid network with node 1 as the source node and node 8 as the destination node.
The question is how to train the model for any destination? I mean the model works well if the destination is say node 8. But I want it to be trained so that it will find the answer if the destination is a different node in the environment. I don't want the destination to be always fixed.
How can I do that?
Here is my code:
#import dependecies
import gym
from gym import Env
from gym.spaces import Discrete
from stable_baselines3 import DQN
from stable_baselines3 import PPO
from stable_baselines3.common.vec_env import DummyVecEnv
from stable_baselines3.common.evaluation import evaluate_policy
import os
#building the environment
class netEnv(Env):
def __init__(self, x, y, sourceNode, destinationNode):
self.action_space = Discrete(4) #0, 1, 2, 3 => up, down, right, left
self.observation_space = Discrete(x*y)
self.sourceNode = sourceNode
self.destinationNode = destinationNode
self.x = x
self.y = y
self.state = self.sourceNode
self.episodeLength = 20
def step(self, action):
#set actions and rewards
if action == 0: #up
if self.state >= self.y:
self.state = self.state - self.y
#print(self.state)
reward = 0.1
else:
reward = 0
elif action == 1: #down
if self.state < (self.x-1)*self.y:
self.state = self.state + self.y
#print(self.state)
reward = 0.1
else:
reward = 0
elif action == 2: #right
if (self.state % self.y) != self.y-1:
self.state += 1
#print(self.state)
reward = 0.1
else:
reward = 0
else: #left
if (self.state % self.y) != 0:
self.state -= 1
#print(self.state)
reward = 0.1
else:
reward = 0
#decrease the remaining time
self.episodeLength -=1
#reward in termination state
if self.state == self.destinationNode:
reward += self.episodeLength
#set done
if self.episodeLength <= 0 or self.state == self.destinationNode:
done = True
else:
done = False
#set info
info = {}
return self.state, reward, done, info
def render(self):
pass
def reset(self):
self.state = self.sourceNode
self.episodeLength = 20
return self.state
env = netEnv(3, 4, 1, 8)
#training
log_path = os.path.join('Training', 'Logs')
model = PPO('MlpPolicy', env, verbose = 1, tensorboard_log = log_path)
model.learn(total_timesteps=15000)
net_path = os.path.join('Training', 'Saved_models', 'net_PPO_15000')
model.save(net_path)
#delete and reload the model
del model
model = PPO.load(net_path, env)
#evaluate the model
print(evaluate_policy(model, env, n_eval_episodes = 10, render = False))

How do I drag and drop specific objects in Love2d?

I'm new to Lua and coding in general so I decided to write a Chess Program to learn. I have setup a class and created objects from it to represent the pieces. Now I want to begin moving the pieces with my mouse. I looked at a tutorial, but it only handled one rectangle. My first though was to use a "for" loop in the love.mousePressed() function to go though each of the objects until it found an object with a matching x, y coordinate. This obviously did not work the way I did it. Instead, it only goes to the next object every time the mouse is pressed or at least it would if the program didn't immediately crash once the button was released. So my question is, what is the right way to be going about this?
local blackPawn = love.graphics.newImage("Textures/Blackpawn.png")
local blackRook = love.graphics.newImage("Textures/Blackrook.png")
local blackKnight = love.graphics.newImage("Textures/Blackknight.png")
local blackBishop = love.graphics.newImage("Textures/Blackbishop.png")
local blackQueen = love.graphics.newImage("Textures/Blackqueen.png")
local blackKing = love.graphics.newImage("Textures/BlackKing.png")
local whitePawn = love.graphics.newImage("Textures/Whitepawn.png")
local whiteRook = love.graphics.newImage("Textures/Whiterook.png")
local whiteKnight = love.graphics.newImage("Textures/Whiteknight.png")
local whiteBishop = love.graphics.newImage("Textures/Whitebishop.png")
local whiteQueen = love.graphics.newImage("Textures/Whitequeen.png")
local whiteKing = love.graphics.newImage("Textures/WhiteKing.png")
local chessboard = love.graphics.newImage("Textures/ChessBoard.png")
local register = {}
local id = 0
piece = {
xSquare = 0, ySquare = 0,
x = 0, y = 0,
height = 64, width = 64,
pawn = false,
Rook = false,
Knight = false,
Bishop = false,
Queen = false,
King = false,
color = "",
texture = whitePawn,
dragging = {active = false, diffx = 0, diffy = 0}
}
function piece.new()
newPiece = {}
for k, v in pairs(piece) do
newPiece[k] = v
end
return newPiece
end
function piece:draw()
end
function getMouse()
local x, y = love.mouse.getPosition()
local isDown = love.mouse.isDown(1,2)
return x, y, isDown
end
function createBoard(id)
for x = 1, 8 do
for y = 1, 8 do
if y ~= 3 and y ~= 4 and y ~=5 and y ~= 6 then
id = id + 1
register[id] = piece.new()
register[id].x = x * 64 - 48
register[id].y = (y - 1) * 64
if y == 2 then
register[id].pawn = true
register[id].color = "white"
register[id].texture = whitePawn
print("item " .. id .. " is here x = " .. register[id].x .. " y = " .. register[id].y .. " Is pawn = " .. tostring(register[id].pawn) ..
" Color is " .. register[id].color)
elseif y == 7 then
register[id].pawn = true
register[id].color = "black"
register[id].texture = blackPawn
print("item " .. id .. " is here x = " .. register[id].x .. " y = " .. register[id].y .. " Is pawn = " .. tostring(register[id].pawn) ..
" Color is " .. register[id].color)
elseif y == 1 then
register[id].color = "white"
if x == 1 or x == 8 then
register[id].Rook = true
register[id].texture = whiteRook
elseif x == 2 or x == 7 then
register[id].Knight = true
register[id].texture = whiteKnight
print("knight is here")
elseif x == 3 or x == 6 then
register[id].Bishop = true
register[id].texture = whiteBishop
elseif x == 5 then
register[id].King = true
register[id].texture = whiteKing
elseif x == 4 then
register[id].Queen = true
register[id].texture = whiteQueen
end
elseif y == 8 then
register[id].color = "black"
if x == 1 or x == 8 then
register[id].Rook = true
register[id].texture = blackRook
elseif x == 2 or x == 7 then
register[id].Knight = true
register[id].texture = blackKnight
elseif x == 3 or x == 6 then
register[id].Bishop = true
register[id].texture = blackBishop
elseif x == 5 then
register[id].King = true
register[id].texture = blackKing
elseif x == 4 then
register[id].Queen = true
register[id].texture = blackQueen
end
end
end
end
end
end
function drawBoard(id, register)
love.graphics.draw(chessboard, 0, 0)
for id = 1, 32 do
love.graphics.draw(register[id].texture, register[id].x, register[id].y)
end
end
function love.load()
createBoard(id)
end
function love.update(dt)
for id = 1, 32 do
if register[id].dragging.active == true then
register[id].x = love.mouse.getX() - register[id].dragging.diffx
register[id].y = love.mouse.getY() - register[id].dragging.diffy
end
end
end
function love.draw()
drawBoard(id, register)
end
function love.mousepressed(x, y, button)
for id = 1, 32 do
if (button == 1 or button == 2)
and x > register[id].x and x < register[id].x + register[id].width
and y > register[id].y and y < register[id].y + register[id].height
then
register[id].dragging.active = true
register[id].dragging.diffx = x - register[id].x
register[id].dragging.diffy = y - register[id].y
end
end
end
function love.mousereleased(x, y, button)
for id = 1, 32 do
if button == 1 or button == 2 then register[id].dragging.active = false end
end
end
function love.keypressed(key, unicode)
end
function love.keyreleased(key)
end
function love.focus(bool)
end
function love.quit()
end
Update:
I fixed the crashing, but I still have the weird bug where it changes the dragged piece into a different piece
Update 2: After a little more debugging I have figured out that the major issue is that it for some reason does not correctly check if the dragging is active. As the code stands right now I need an else dragging.active = false after to correctly set it, but now that it is correctly set it won't drag anything at all despite the correct object have dragging set to active (unless I try and drag the object with value 32 where it drags everything at once). I am very confused as to what's wrong. Why isn't Lua able to check value like this?
First, I'd create a global boolean for if a piece has been selected and then a variable to hold the piece selected
local selected = false
local selectedPiece = {}
Then create a playing board and split it into a grid, with each square being of equal size. Something like this
board = {
size = { 8, 8 }, -- 8x8 grid
squareSize = 40, -- 40 pixels long sides
pieces = {
{ -- First row contains which pieces?
Piece:Rook(),
Piece:Bishop(),
...
},
{ -- Second row
Piece:Pawn(),
...
},
{ -- etc.
Piece:Empty(),
...
}
}
}
I advise not using nil in your table for empty squares due to the odd behavior of tables with nil indexes.
In your love.mousepressed() method, you check where the click was based on its position (this is assuming the board takes up the whole window)
function love.mousepressed(x, y, btn)
-- If a piece hasn't been clicked on.
if (not selected) then
-- This line is assuming that since all board squares are equal size, then the mouse click has to be in at least one square.
-- Therefore, if we take the floor of the position/board.squareSize, we will always get a value from 0 - 7 (8 values) on the board.
local piece = board.pieces[math.floor(x/board.squareSize)][math.floor(y/board.squareSize)]
-- If there is a piece here.
if (piece:isNotAnEmpty()) then
selectedPiece = piece -- Select the piece.
selected = not selected -- Notify program that a piece is selected to handle such things accordingly in other methods.
end
else
-- Assuming you wrote a method that determines if a piece can be moved to a certain spot on the board.
if (board:CanMovePieceHere(selectedPiece, x/board.size, y/board.size)) then
-- Do your stuff here.
...
-- Eventually, reset your variables.
selected = not selected
selectedPiece = {}
end
end
end
This is how I'd approach it, but your question is very open to interpretation.

My Lua maze builder that is braiding

I am building a Lua script that generates a maze using a version of the Recursive Backtracker implemented with a stack rather than recursion. Presently the maze is coming out braided and I can't seem to figure out where in my logic this is happening. The function below takes in x and y as a starting point for generating the maze which is a 2d structure (table of tables):
local function buildMazeInternal(x,y,maze)
local stack = {}
local directions = {'North','East','South','West'}
table.insert(stack,{x=x,y=y})
while #stack > 0 do
local index = 1
local nextX = x
local nextY = y
local braid = false
for i = #directions, 2, -1 do -- backwards
local r = calc:Roll(1,i) -- select a random number between 1 and i
directions[i], directions[r] = directions[r], directions[i] -- swap the randomly selected item to position i
end
while index <= #directions and nextX == x and nextY == y do
if directions[index] == 'North' and y > 1 and not maze[y-1][x].Visited then
maze[y][x].North = true
maze[y-1][x].South = true
nextY = y-1
elseif directions[index] == 'East' and x < width and not maze[y][x+1].Visited then
maze[y][x].East = true
maze[y][x+1].West = true
nextX = x+1
elseif directions[index] == 'South' and y < height and not maze[y+1][x].Visited then
maze[y][x].South = true
maze[y+1][x].North = true
nextY = y+1
elseif directions[index] == 'West' and x > 1 and not maze[y][x-1].Visited then
maze[y][x].West = true
maze[y][x-1].East = true
nextX = x-1
else
index = index + 1
end
end
if nextX ~= x or nextY ~= y then
x = nextX
y = nextY
maze[y][x].Visited = true
table.insert(stack,{x=x,y=y})
else
x = stack[#stack].x
y = stack[#stack].y
table.remove(stack)
end
end
end
I know I'm overlooking something but I can't seem to nail it down. Note that the calc:Roll(1,100) method is a .net method in my app used to simulate rolling dice, in this case 1 * 100 sided die, it could be replaced with a call to math.Random(1,100) for use outside of my application.
I see at least one problem. When you go "want to go up", you check whether the "cell which is up" was visited, and if it is, you "skip" going up.
This does not seem to be correct IMHO. If you want to go up, but the cell which is "up" from the current cell was visited but has a "down" exit, you should still be able to go up (instead of skipping because it is visited).
The same applies to the other directions.
That's all I got.
I found the answer after posting on Reddit. I wasn't setting the initial cell to visited allowing it to be passed through twice. The correction was to add maze[y][x].Visited = true immediately before table.insert(stack,{x=x,y=y}).

Using collide_widget so widgets don't overlap, what is the best solution here?

I have a character that runs around the screen, and an image of a tree. I have collide_widget working so that when the character runs into the tree, it returns True. The problem is that I am trying to stop the character from running over the tree, the tree should be blocking the characters movement. What I have tried to do was stop the characters movement to the left, when running into the tree from the left. When I do this it works and I can then use the up, down or, right key to run away from the tree. However, if I use this code for all directions, it prevents me from being able to walk away from the tree in any direction once the collide_widget(tree) is True. I understand the problem, but I am not sure the solution. Is there a better way to do this? Here is part of the code. Is there maybe away to just stop movement before the collision happens?
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
print self.pos
print self.app.tree.pos
self.source = 'selectionscreen/left.zip'
self.anim_delay=.20
if self.x < (Window.width * .25):
bglayout.x += 4
else: #collide widget code
if self.collide_widget(self.app.tree)
self.x -= 0
else:
self.x -= 6
elif keycode[1] == 'right':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.x > (Window.width * .70):
bglayout.x -= 4
else:
self.x += 6
elif keycode[1] == 'down':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.y < (Window.height * .25):
bglayout.y += 4
else:
self.y -= 6
elif keycode[1] == 'up':
self.source = 'selectionscreen/back.zip'
self.anim_delay=.1
if self.y > (Window.height * .70):
bglayout.y -= 4
else:
self.y += 6
else:
return False
return True
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'left':
self.source = 'selectionscreen/left1.png'
elif keycode[1] == 'right':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'down':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'up':
self.source ='selectionscreen/back2.png'
else:
return False
return True
you can copy x and y first (to previous x and previous y perhaps), move the player, check if collideWidget is true, and if so, assign the old values from before you moved and it will be like nothing happened.

How to control more than one image position with Kivy

I am making a game with Kivy where you can move a character around the screen. Earlier, I had it working where when you move the character right, the background image would scroll to the left, and same with the right. Recently, what I tried to do was add the background image to a widget along with another image, with the intention that i could control multiple image locations with 'widget.pos', but as soon as I did this, not only does the second image (hero2) not scroll right or left but neither does the background image. I am wondering what I did wrong in my code here.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.core.audio import SoundLoader
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import FallOutTransition
gamelayout = FloatLayout(size=(300, 300))
class Game(Screen):
pass
class IconButton(ButtonBehavior, Image):
pass
class Bg(Image):
def __init__(self, **kwargs):
super(Bg, self).__init__(**kwargs)
self.allow_stretch = True
self.size_hint = (None, None)
self.size = (1440, 1440)
class MoveableImage(Image):
def __init__(self, **kwargs):
super(MoveableImage, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(None, self)
if not self._keyboard:
return
self._keyboard.bind(on_key_down=self.on_keyboard_down)
self._keyboard.bind(on_key_up=self.on_keyboard_up)
self.size_hint = (.11, .11)
self.y = (Window.height/2.1)
self.app = App.get_running_app()
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
self.source = 'selectionscreen/left.zip'
self.anim_delay=.20
if self.x < (Window.width * .25):
self.app.test.x += 4
else:
self.x -= 6
elif keycode[1] == 'right':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.x > (Window.width * .70):
self.app.test.x -= 4
else:
self.x += 6
elif keycode[1] == 'down':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.y < (Window.height * .25):
self.app.test.y += 4
else:
self.y -= 6
elif keycode[1] == 'up':
self.source = 'selectionscreen/back.zip'
self.anim_delay=.1
if self.y > (Window.height * .70):
self.app.test.y -= 4
else:
self.y += 6
else:
return False
return True
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'left':
self.source = 'selectionscreen/left1.png'
elif keycode[1] == 'right':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'down':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'up':
self.source ='selectionscreen/back2.png'
else:
return False
return True
class gameApp(App):
def build(self):
global sm
sm = ScreenManager()
game = Game(name='game')
sm.add_widget(game)
hero = MoveableImage(source='selectionscreen/jeezyright1.png', pos=(75, 40))
self.hero2 = Image(source='selectionscreen/hero2.zip', pos=(100, 200))
self.background=Bg(source='selectionscreen/background9.png')
self.test=Bg()
self.test.add_widget(self.background)
self.test.add_widget(self.hero2)
gamelayout.add_widget(self.test)
gamelayout.add_widget(hero)
game.add_widget(gamelayout)
return sm
if __name__ == '__main__':
gameApp().run()
Looks like the problem here is that you are using a FLoatLayout. When you move a normal layout (such as a FloatLayout), it does not automatically change the children's co-ordinates. If you want that behavior, try using a RelativeLayout.
http://kivy.org/docs/api-kivy.uix.relativelayout.html
Peace out

Resources