Better way to write this code - ruby

I am designing a user interface for a menu project. I tried using a for-loop such as:
for i in 0..8
i=i
end
for k in 0..7
k=k
end
if #selection==i && #unlock==k && $switches[(what do I do here?)]==?????
do thing
Whenever the user presses the Y key, it will turn off a function; if #selection==1 is highlighted and the user presses the "Y" key, the corresponding switch at that specific location should be turned off. #unlock is just used as a way of saying that, unless this global boolean is set to true, the user can press "Y" and turn this switch on or off.

First thing, you could change each if else to something like this:
BITMAP_PATH = "Graphics/Pictures/Input Map/switch"
if #selection==1 && #unlock1
pbSEPlay("BW2MenuChoose",65)
bitmap_switch = $switches[310] ? 'off' : 'on' # sets path to off/on
#graphics["switch"].setBitmap(BITMAP_PATH + bitmap_switch)
!$switches[310] # it changes value of boolean to opposite value
end
And the selections that only have one condition could be written like this:
if #selection==0 && #unlock0
pbSEPlay("buzzer",65)
end
You could also try writing a case expression for #selection. Probably you could dry it even more, but I do not really understand what each #unlock is used for.
Edit:
BITMAP_PATH = "Graphics/Pictures/Input/switch"
SELECTION_SWITCHES = [nil, 310, 300, 339, 338, 330, 318]
def pbChangeSwitch
case
when 0
case #selection
when 0,7
pbSEPlay("buzzer",65) if instance_variable_get("#unlock#{#selection}")
when 1..6
if instance_variable_get("#unlock#{#selection}")
pbSEPlay("BW2MenuChoose",65)
bitmap_switch = $switches[SELECTION_SWITCHES[#selection]] ? 'off' : 'on'
#sprites["switch"].setBitmap(BITMAP_PATH + bitmap_switch)
index = SELECTION_SWITCHES[#selection]
$switches[index] = !$switches[index]
end
end
Graphics.update
Please add $ to the last line. bitmap_switch can not be true or false, because you add it to BITMAP_PATH, so it has to be 'off' or 'on'.

Related

How to create a Roblox game where the player has to guess a randomly generated pin?

So, I've been working on this for the past week. I have tried everything (based on the knowledge I know) and yet nothing... my code didn't work the first time, the second time, the third time... the forth... etc... at the end, I let frustration take control of me and I ended up deleting the whole script. Luckily not the parts and models, otherwise I'm really screwed...
I need to create a game in which I have to create a keypad of sorts, at first I thought GUI would work... no, it needs to be SurfaceGUI, which I don't know how to handle well... Anyway, I needed to create a keypad using SurfaceGUI, and display it on a separate screen, as a typical keypad would...
The Player would first have to enter an "initial" number, meaning in order to enter the randomly generated number he first needed to input the static pin in order to "log in," after that, then he would try to guess the number...
I've literally tried everything I could but nothing... It's mainly because of my lack of experience in LUA, I'm more advanced in Python and barely know a thing in Java... If someone could assist me on how to do this, I would appreciate it greatly
First, download this and put it in a ScreenGui in StarterGui. Then, use the following LocalScript placed inside the PIN frame:
-- Script settings
local len = 4 -- replace this as needed...
local regen = false -- determines whether PIN will regenerate after a failed attempt
local regmes = "Enter PIN..." -- start message of PIN pad
local badmes = "Wrong PIN!" -- message displayed when PIN is wrong
local success = "Correct PIN!" -- message displayed when PIN is right
-- Script workings
local pin = script.Parent
local top = pin.Top
local txt = top.Numbers
local nums = top.NumKeys
local pin
local stpin
local nms
txt.Text = regmes
local see = game:GetStorage("ReplicatedStorage").PINActivate
local function activate()
if pin.Visible then
pin.Visible = false
for _, btn in pairs(nums:GetChildren()) do
btn.Active = false
end
return
else
pin.Visible = true
for _, btn in pairs(nums:GetChildren()) do
btn.Active = true
end
return
end
end
local function rand()
math.randomseed(os.time) -- better random numbers this way
return tostring(math.floor(math.random(0,9.9)))
end
local function gen()
nms = {rand()}
for i=2, len, 1 do
nms[#nms+1]=rand()
end
stpin = nms[1]
for i=2, #nms, 1 do
stpin = stpin..nms[i]
end
pin = tonumber(stpin) -- converts the number string into an actual number
end
gen()
local function activate(str)
if tonumber(str) ~= pin then
txt.Text = badmes
wait(2)
txt.Text = regmes
if regen then
gen()
wait(0.1)
end
return
else
txt.Text = success
wait(2)
activate()
-- insert code here...
end
end
for _, btn in pairs(nums:GetChildren()) do
btn.Activated:Connect(function()
if txt.Text == "Wrong PIN!" then return end
txt.Text = txt.Text..btn.Text
if string.len(txt.Text) >= len then
activate(txt.Text)
end
wait(0.1)
end)
end
see.OnClientEvent:Connect(activate)
And in a Script put this:
local Players = game:GetService("Players")
local see = game:GetService("ReplicatedStorage").PINActivate
local plr
-- replace Event with something like Part.Touched
Event:Connect(function(part)
if part.Parent.Head then
plr = Players:GetPlayerFromCharacter(part.Parent)
see:FireClient(plr)
end
end)
What this will do is bring up a ScreenGui for only that player so they can enter the PIN, and they can close it as well. You can modify as needed; have a great day! :D
There is an easier way, try this
First, Create a GUI in StarterGui, then, Create a textbox and postion it, after that, create a local script inside and type this.
local Password = math.random(1000, 9999)
print(Password)
game.ReplicatedStorage.Password.Value = Password
script.Parent.FocusLost:Connect(function(enter)
if enter then
if script.Parent.Text == tostring(Password) then
print("Correct!")
script.Parent.BorderColor3 = Color3.new(0, 255, 0)
Password = math.random(1000, 9999)
game.ReplicatedStorage.Correct1:FireServer()
print(Password)
game.ReplicatedStorage.Password.Value = Password
else
print("wrong!")
print(script.Parent.Text)
script.Parent.BorderColor3 = Color3.new(255, 0, 0)
end
end
end)
That's all in the textbox.
Or if you want a random username script, create a textlabel, then, create a local script in the textlabel and type in this.
local UserText = script.Parent
local Username = math.random(1,10)
while true do
if Username == 1 then
UserText.Text = "John"
elseif Username == 2 then
UserText.Text = "Thomas"
elseif Username == 3 then
UserText.Text = "Raymond"
elseif Username == 4 then
UserText.Text = "Ray"
elseif Username == 5 then
UserText.Text = "Tom"
elseif Username == 6 then
UserText.Text = "Kai"
elseif Username == 7 then
UserText.Text = "Lloyd"
elseif Username == 8 then
UserText.Text = "Jay"
elseif Username == 9 then
UserText.Text = "User"
else
UserText.Text = "Guest"
end
wait()
end
All of those if statments are checking what username has been chosen. I have made a roblox game like this recently, so I just took all the script from the game.
If you want to check out my game, Click Here

Entry validation fails when replacing spaces with empty strings

I am not sure why validation stops working after I end up replacing spaces in a string.
The validation works fine for most things I need. It allows for only numbers and up to 10 of them while also allowing for back spaces and highlighting all and back spacing. It also works once when pasting in values that contain spaces.
For example if I try to paste 12 34 into the entry field it will correctly change the value to 1234 but after that validation just stops working.
Update:
It appears that validation stops working after entry.delete() though I still am not sure how to correct this. I have tried to redefine the validation but that did not work.
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.vcmd = (self.register(self.validate), '%d', '%P', '%s')
self.entry = tk.Entry(self, validate='key', validatecommand=self.vcmd)
self.entry.pack()
def validate(self, *a):
b = a[1].replace(' ', '')
if b.isdigit() and len(b) <= 10 or ((b == '' or b < a[2]) and a[0] == '0'):
if ' ' in a[1]:
x = a[1]
x = x.replace(' ', '')
self.entry.delete(0, 'end')
self.entry.insert(0, x)
return True
else:
return False
App().mainloop()
Update:
I managed to get it working by deleting the entry field and redefining it and its validation after inserting the new string. But this seams like the wrong way to do this.
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.vcmd = (self.register(self.validate), '%d', '%P', '%s')
self.entry = tk.Entry(self, validate='key', validatecommand=self.vcmd)
self.entry.pack()
def validate(self, *a):
b = a[1].replace(' ', '')
if b.isdigit() and len(b) <= 10 or ((b == '' or b < a[2]) and a[0] == '0'):
if ' ' in a[1]:
x = a[1]
x = x.replace(' ', '')
self.entry.destroy()
self.entry = tk.Entry(self)
self.entry.insert(0, x)
self.entry.config(validate='key', validatecommand=self.vcmd)
self.entry.pack()
return True
else:
return False
App().mainloop()
I am not sure why validation stops working after I end up replacing spaces in a string. The validation works fine for most things I need.
It stops working because that's how it is designed to work. If you try to modify the data from within the validation function, the validate option is automatically reset to "none".
This is what the official tcl/tk documentation says:
The validate option will also set itself to none when you edit the entry widget from within either the validateCommand or the invalidCommand.
You will need to reset the validate option in the case where you modify the widget from within the validation function.

Tool/Algorithm for text comparision after every key hit

I am struggling to find a text comparison tool or algorithm that can compare an expected text against the current state of the text being typed.
I will have an experimentee typewrite a text that he has in front of his eyes. My idea is to compare the current state of the text against the expected text whenever something is typed. That way I want to find out when and what the subject does wrong (I also want to find errors that are not in the resulting text but were in the intermediate text for some time).
Can someone point me in a direction?
Update #1
I have access to the typing data in a csv format:
This is example output data of me typing "foOBar". Every line has the form (timestamp, Key, Press/Release)
17293398.576653,F,P
17293398.6885,F,R
17293399.135282,LeftShift,P
17293399.626881,LeftShift,R
17293401.313254,O,P
17293401.391732,O,R
17293401.827314,LeftShift,P
17293402.073046,O,P
17293402.184859,O,R
17293403.178612,B,P
17293403.301748,B,R
17293403.458137,LeftShift,R
17293404.966193,A,P
17293405.077869,A,R
17293405.725405,R,P
17293405.815159,R,R
In Python
Given your input csv file (I called it keyboard_records.csv)
17293398.576653,F,P
17293398.6885,F,R
17293399.135282,LeftShift,P
17293399.626881,LeftShift,R
17293401.313254,O,P
17293401.391732,O,R
17293401.827314,LeftShift,P
17293402.073046,O,P
17293402.184859,O,R
17293403.178612,B,P
17293403.301748,B,R
17293403.458137,LeftShift,R
17293404.966193,A,P
17293405.077869,A,R
17293405.725405,R,P
17293405.815159,R,R
The following code does the following:
Read its content and store it in a list named steps
For each step in steps recognizes what happened and
If it was a shift press or release sets a flag (shift_on) accordingly
If it was an arrow pressed moves the cursor (index of current where we insert characters) – if it the cursor is at the start or at the end of the string it shouldn't move, that's why those min() and max()
If it was a letter/number/symbol it adds it in curret at cursor position and increments cursor
Here you have it
import csv
steps = [] # list of all actions performed by user
expected = "Hello"
with open("keyboard.csv") as csvfile:
for row in csv.reader(csvfile, delimiter=','):
steps.append((float(row[0]), row[1], row[2]))
# Now we parse the information
current = [] # text written by the user
shift_on = False # is shift pressed
cursor = 0 # where is the cursor in the current text
for step in steps:
time, key, action = step
if key == 'LeftShift':
if action == 'P':
shift_on = True
else:
shift_on = False
continue
if key == 'LeftArrow' and action == 'P':
cursor = max(0, cursor-1)
continue
if key == 'RightArrow' and action == 'P':
cursor = min(len(current), cursor+1)
continue
if action == 'P':
if shift_on is True:
current.insert(cursor, key.upper())
else:
current.insert(cursor, key.lower())
cursor += 1
# Now you can join current into a string
# and compare current with expected
print(''.join(current)) # printing current (just to see what's happening)
else:
# What to do when a key is released?
# Depends on your needs...
continue
To compare current and expected have a look here.
Note: by playing around with the code above and a few more flags you can make it recognize also symbols. This will depend on your keyboard. In mine Shift + 6 = &, AltGr + E = € and Ctrl + Shift + AltGr + è = {. I think this is a good point to start.
Update
Comparing 2 texts isn't a difficult task and you can find tons of pages on the web about it.
Anyway I wanted to present you an object oriented approach to the problem, so I added the compare part that I previously omitted in the first solution.
This is still a rough code, without primary controls over the input. But, as you asked, this is pointing you in a direction.
class UserText:
# Initialize UserText:
# - empty text
# - cursor at beginning
# - shift off
def __init__(self, expected):
self.expected = expected
self.letters = []
self.cursor = 0
self.shift = False
# compares a and b and returns a
# list containing the indices of
# mismatches between a and b
def compare(a, b):
err = []
for i in range(min(len(a), len(b))):
if a[i] != b[i]:
err.append(i)
return err
# Parse a command given in the
# form (time, key, action)
def parse(self, command):
time, key, action = command
output = ""
if action == 'P':
if key == 'LeftShift':
self.shift = True
elif key == 'LeftArrow':
self.cursor = max(0, self.cursor - 1)
elif key == 'RightArrow':
self.cursor = min(len(self.letters), self.cursor + 1)
else:
# Else, a letter/number was pressed. Let's
# add it to self.letters in cursor position
if self.shift is True:
self.letters.insert(self.cursor, key.upper())
else:
self.letters.insert(self.cursor, key.lower())
self.cursor += 1
########## COMPARE WITH EXPECTED ##########
output += "Expected: \t" + self.expected + "\n"
output += "Current: \t" + str(self) + "\n"
errors = UserText.compare(str(self), self.expected[:len(str(self))])
output += "\t\t"
i = 0
for e in errors:
while i != e:
output += " "
i += 1
output += "^"
i += 1
output += "\n[{} errors at time {}]".format(len(errors), time)
return output
else:
if key == 'LeftShift':
self.shift = False
return output
def __str__(self):
return "".join(self.letters)
import csv
steps = [] # list of all actions performed by user
expected = "foobar"
with open("keyboard.csv") as csvfile:
for row in csv.reader(csvfile, delimiter=','):
steps.append((float(row[0]), row[1], row[2]))
# Now we parse the information
ut = UserText(expected)
for step in steps:
print(ut.parse(step))
The output for the csv file above was:
Expected: foobar
Current: f
[0 errors at time 17293398.576653]
Expected: foobar
Current: fo
[0 errors at time 17293401.313254]
Expected: foobar
Current: foO
^
[1 errors at time 17293402.073046]
Expected: foobar
Current: foOB
^^
[2 errors at time 17293403.178612]
Expected: foobar
Current: foOBa
^^
[2 errors at time 17293404.966193]
Expected: foobar
Current: foOBar
^^
[2 errors at time 17293405.725405]
I found the solution to my own question around a year ago. Now i have time to share it with you:
In their 2003 paper 'Metrics for text entry research: An evaluation of MSD and KSPC, and a new unified error metric', R. William Soukoreff and I. Scott MacKenzie propose three major new metrics: 'total error rate', 'corrected error rate' and 'not corrected error rate'. These metrics have become well established since the publication of this paper. These are exaclty the metrics i was looking for.
If you are trying to do something similiar to what i did, e.g. compare the writing performance on different input devices this is the way to go.

For-loop in lua with löve2D, deleting variable

i'm a bit beginner about coding, and my english isn't great, i hope i'll be able to make my question clear:
So I have a for-loop in my code, and 2 if in it, in each if, I see if something is true or false, if it's true, I delete a portion of the loop. like that:
for n=#Test,1, -1 do
if Test[n].delete == true then
table.remove(Test, n )
end
if Test[n].y > 0 then
table.remove(Test, n )
end
end
kind of like that, and my problem is, if the first if make Test[n] being deleted, then the game crash at the next if because Test[n] Doesn't exist anymore. I solved the problem by making 2 for-loop, one for each if.
But I saw someone who did it without 2 for-loop, and it's not crashing, I tried to figure what was wrong but I can't find it.
If someone could find what is wrong with what I wrote, I would be thankful!
So here is the moment that makes problem in my code, on line 9, if the condition are met, i table.remove(ListeTirs, n ), then on line 17, when code try to find it again for test it, it bug :
for n=#ListeTirs,1, -1 do
if ListeTirs[n].Type == "Gentils"
then local nAliens
for nAliens=#ListeAliens,1,-1 do
if
Collide(ListeTirs[n], ListeAliens[nAliens]) == true
then
ListeTirs[n].Supprime = true
table.remove(ListeTirs, n )
ListeAliens[nAliens].Supprime = true
table.remove(ListeAliens, nAliens)
end
end
end
if
ListeTirs[n].y < 0 - ListeTirs[n].HauteurMoitie or ListeTirs[n].y > Hauteur + ListeTirs[n].HauteurMoitie or ListeTirs[n].x > Largeur + ListeTirs[n].LargeurMoitie or ListeTirs[n].x < 0 - ListeTirs[n].LargeurMoitie
then
ListeTirs[n].Supprime = true
table.remove(ListeTirs, n)
end
end
I hope it's clear, I could post the whole code but I don't feel it's necessary, if it is, I will add it.
Thank you :)
for n=#Test,1, -1 do
local should_be_removed
-- just mark for deletion
if Test[n].delete = true then
should_be_removed = true
end
-- just mark for deletion
if Test[n].y > 0 then
should_be_removed = true
end
-- actually delete (at the very end of the loop body)
if should_be_removed then
table.remove(Test, n )
end
end

Force to end n if in haml

I have an if:
-12.times do |control|
-dia += 1
-if control == 1
%a#hoy{:href=>'/dias/algo'}<
-else
%a{:href=>'/dias/algo'}<
=dia
%span=dias[rand(7)]
The problem is I need =dia and span elements inside the anchor tag in both cases (true/false), and when I quit one identation it fails, because haml will end the if (which is also normal).
Is there any way to force end an if? I have tried it in many ways, but couldn't find the right way if it exist.
Thanks.
-12.times do |control|
-dia += 1
%a{:id => control == 1 ? "hoy" : "", :href=>'/dias/algo'}<
=dia
%span=dias[rand(7)]
Didn't test it but it should work ...

Resources