I'm working on an 'impossible' game, where you are basically asked a bunch of trick questions.
The first time I run the script, everything works perfectly. If the user decides to play again by typing in 'y', it will re-run the mainScript function. However, the script automatically re-runs the mainScript after finishing it a second time, without taking user input. I might be making a simple mistake, I'm not sure. Here is the script: (Sorry, I know it is kind of long)
math.randomseed(os.time())
local lives = 3
local points = 0
Questions = {
{"What is the magic word?", "A) Please", "B) Abra-Cadabra", "C) Lotion", "D) Cheese", "c"},
{"Does anyone love you?", "A) Yes", "B) No", "C) Everyone loves me!", "D) My mother does", "b"},
{"How many fingers do you have?", "A) None", "B) Eight", "C) Seventeen", "D) Ten", "d"},
{"What is 1 + 1?", "A) Window", "B) Door", "C) Two", "D) That's a stupid question", "a"}
}
savedQuestions = {} --Load the Questions table into savedQuestions
for i, v in pairs(Questions) do
table.insert(savedQuestions, v)
end
function loadTable() --Load the savedQuestions into Questions
for i = 1, #savedQuestions do
table.insert(Questions, savedQuestions[i])
end
end
function waitForStart()
local chk = io.read() tostring(chk)
if (chk:sub(1, 5)):lower() == "start" then
return true
end
waitForStart()
end
function lookForAnswer(ans)
table.remove(Questions, number)
local input = io.read() tostring(input)
if input:lower() == ans then
points = points + 1
return true
end
lives = lives - 1
return false
end
function mainScript()
lives = 3
points = 0
print("Welcome to the Impossible quiz!")
print("Type 'start' when you are ready to begin\n")
waitForStart() io.write("\n")
for i = 1, #Questions do
number = math.random(1, #Questions)
local prob = Questions[number]
local q = prob[1]
local a = prob[6]
print(q)
print(prob[2] .. "\n" .. prob[3] .. "\n" .. prob[4] .. "\n" .. prob[5] .. "\n")
if lookForAnswer(a) then
print("Correct! Points: " .. points .. " Lives: " .. lives .. "\n\n")
else
print("WRONG! Points: " .. points .. " Lives: " .. lives .. "\n\n")
if lives <= 0 then
return false
end
end
end
return true
end
function checkForReplay()
print("Would you like to play again? (Y / N)")
local chk = io.read() tostring(chk)
if (chk:sub(1, 1)):lower() == "y" then
return true
end
return false
end
function checkWin()
if mainScript() then
print("You won!")
print("Points: " .. points .. "\n")
if checkForReplay() then
Questions = {}
loadTable()
mainScript()
else
exit()
end
else
print("You lose!")
print("Points: " .. points .. "\n")
if checkForReplay() then
Questions = {}
loadTable()
mainScript()
else
exit()
end
end
end
while true do
checkWin()
end
You should factor out the "startover" logic into the loop, you would see better. Then you notice how there is common code in both if chunks of your checkWin, factor that out too:
function checkWin()
if mainScript() then
print("You won!")
else
print("You lose!")
end
print("Points: " .. points .. "\n")
if not checkForReplay() then
exit()
end
end
while true do
checkWin()
-- if you get here you have not exited so start over:
Questions = {}
loadTable()
mainScript() -- oops! what's that doing here?
end
Note also that it is better to let the script return than call os.exit() (assuming that is what exit() is in your code -- see for example How to terminate Lua script?):
function checkWin()
if mainScript() then
print("You won!")
else
print("You lose!")
end
print("Points: " .. points .. "\n")
return checkForReplay()
end
local playAgain = checkWin()
while playAgain do
Questions = {}
loadTable()
playAgain = checkWin()
end
Related
I'm making an action RPG in Love2d, and I moved all of my player code into a separate Lua script for the sake of organization - but now upon attempting to use player.load, I get this error:
Error
main.lua:22: attempt to index global 'player' (a boolean value)
Traceback
main.lua:22: in function 'load'
[C]: in function 'xpcall'
[C]: in function 'xpcall'
this is my main.lua script:
-- RPG PROJECT IN LOVE2D
-- debug mode
debug = true -- SET FALSE BEFORE SHIPPING
-- ROOM CHART:
-- 0 - Title
-- 1 - Overworld
-- 2 - Shop
-- 3 - Boss Room
Room = 0
-- PLAYER STATE CHART:
-- 0 - free
-- 1 - attacking
-- 3 - can't move
function love.load(dt)
player = require 'Scripts/player'
player.load()
sti = require 'Libraries/sti'
gameMap = sti('Maps/overworld.lua')
menuImg = love.graphics.newImage('Assets/Menu.png')
love.window.setMode(800, 600)
end
function love.draw(dt)
if Room == 0 then
love.graphics.draw(menuImg, 0, 0)
end
if Room == 1 then
gameMap:draw()
player.draw()
end
if debug == true then
love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 10, 10)
end
end
function love.update(dt)
if Room == 0 then
if love.keyboard.isDown('space') then
Room = 1
end
end
if Room == 1 then
player.Update(dt)
if love.keyboard.isDown('escape') then
Room = 0
end
end
if love.keyboard.isDown('t') then
debug = not debug
end
end
and this is my player.lua script:
-- PLAYER SCRIPT
player = {x = 50, y = 50, speed = 3, state = 0, img = nil}
function player.load()
playerRight = love.graphics.newImage('Assets/playerRight.png')
playerLeft = love.graphics.newImage('Assets/playerLeft.png')
playerUp = love.graphics.newImage('Assets/playerUp.png')
playerDown = love.graphics.newImage('Assets/playerDown.png')
player.img = playerDown
end
function GetInput()
if player.state == 0 or player.state == 1 then
if love.keyboard.isDown('w') then
player.y = player.y - player.speed
player.img = playerUp
elseif love.keyboard.isDown('s') then
player.y = player.y + player.speed
player.img = playerDown
end
if love.keyboard.isDown('a') then
player.x = player.x - player.speed
player.img = playerLeft
elseif love.keyboard.isDown('d') then
player.x = player.x + player.speed
player.img = playerRight
end
end
end
function player.update(dt)
GetInput()
end
function player.draw()
love.graphics.draw(player.img, player.x, player.y)
end
Any help would be much appreciated!
Here's my folders as well, just in case it's a path issue:
UPDATE:
I solved it by renaming the player object to oPlayer, that was what was giving me an error.
I have severall Hints for you
Unclear
It looks you write it under Linux but should it also run under Windows?
If so, use OS independent folder delimiter ( the dot ) in require().
Example
player = require 'Scripts.player'
In General: Dont use Slash / Backslash \ in require()
Tip: LÖVE uses LuaJIT with modified Lua 5.1 check the Windows OS is easy
(For deciding to use Slash or Backslash (not in/for require()))
Clear
As #Luke100000 mentioned...
A Script for require has to return something.
If not than only a true wil be cached and a next require returns only true.
Therefore your player.lua content should be...
-- player.lua
local player = {x = 50, y = 50, speed = 3, state = 0, img = nil}
-- player.load() will be local
-- GetInput() wil be global
-- And the last line...
return(player)
I'm trying to get my save_game() method to work, nothing is getting written to the JSON file. This is my first assignment working with JSON and serialization in general. I'm not quite sure where I'm even going wrong.
These are my serialization methods:
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
This is my program, on line 92 I try to call my save_game method.
require 'json'
load 'display.rb'
class Hangman
attr_accessor :name
#name = name
def initialize
puts "What is your name?"
#name = gets.chomp
puts "
################################################
HANGMAN
################################################
_________
|
| |
| O
| /|\\
| |
| / \\
|
-----------------
Welcome #{#name} to Hangman. The computer will generate
a 5-12 letter random word. You will try to guess
that word one letter at a time. Try to solve the
puzzle before time runs out!
"
end
end
class Gameplay
attr_accessor :array, :filestuff, :random_word, :cipher, :random_word2, :counter
def initialize
#array = []
#filestuff = File.foreach('5text.txt') do |x|
chomped = x.chomp
#array << chomped if (chomped.length >= 5 and chomped.length <= 12)
end
#random_word = #array.sample
#cipher = #random_word.gsub(/[a-z]/, '*').split(//)
#random_word2 = #random_word.split(//)
#counter = 5
def to_json
JSON.generate({array: #array, filestuff: #filestuff, random_word: #random_word, cipher: #cipher, random_word2: #random_word2, counter: #counter})
end
def load
game_file = File.read("saved.json")
data = JSON.parse(game_file)
#cipher = data["cipher"]
#random_word2 = data["random_word2"]
#counter = data["counter"]
end
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
def choice(n)
#random_word2.each_with_index do |i,index|
if i == n
#cipher[index] = i
end
end
if n == #random_word2.join.to_s
puts "You win"
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
Hangman.new
else exit
end
end
if #random_word2.include?(n) == false
#counter -= 1
display
puts "#{#counter} guesses remaining."
puts "To save press 1"
save = gets.chomp
if save == "1"
#Will not save
save_game($b.to_json)
end
end
if #counter == 0
puts "would you like to start another game? Y/N"
new_game = gets.chomp
if new_game == "Y"
else exit
end
end
puts #cipher.join
end
#counter = 5
while #counter > 0
choice(gets.chomp)
end
end
end
Hangman.new
$b = Gameplay.new
You need to close the file in order to make sure your output is actually written to the disk ("flushed"). You can manually, call close:
def save_game(string)
game_file = File.new("saved.json","w")
game_file.write(string)
game_file.close
end
or, you can use File.open, which takes a block and closes the file when the block ends:
File.open("saved.json", "w") do |game_file|
game_file.write(string)
end
Since, writing to the disk is a slow operation, Ruby (and all languages that I can think of right now) will hold off on actually writing the file until it has accumulated a certain amount of text in a buffer. Once it has reached this limit, it will flush the buffer and write everything in it to disk. In order to make sure all your text is actually written when trying to write a file, you need to call close on the file, and as part of closing it, Ruby will flush whatever is left in its buffer.
There are other ways of making sure your content is flushed but when you're just starting to learn about this stuff, it should suffice to just make sure to always close files when you're done reading or writing them.
I am new to programming (About 2 months in to teaching myself). I am attempting to create a game of tic tac toe and use it as an opportunity to practice using functions and passing parameters.
I have got most of what I want to work working (for now I will add AI and a computer opponent) but when one of the human players win the endgame() function is called but it does not work as expected. It calls itself somehow and you have got to say N to end the game three times before the program is terminated. I am unable to see the wood for the trees on this one folks so help would be appreciated.
I know some of my coding will not be great so no trolling please.
Thanks
Shaun
def start():
choices = [" "," "," "," "," "," "," "," "," "]
while checkwin(choices)==False:
board(choices)
getchoice(choices)
def board(choices):
print("+---+---+---+")
print("+ "+choices[0]+" + "+choices[1]+" + "+choices[2]+" +")
print("+---+---+---+")
print("+ "+choices[3]+" + "+choices[4]+" + "+choices[5]+" +")
print("+---+---+---+")
print("+ "+choices[6]+" + "+choices[7]+" + "+choices[8]+" +")
print("+---+---+---+")
def endgame(winner):
print("The winner is "+ winner)
playagain=input("Another game? Y/N")
playagain = playagain.upper()
if playagain == "N":
print("Thanks, hope to see you again soon.")
else:
start()
def getchoice(choices):
userchoice = int(input ("Where would you like to put your X?"))
index = userchoice-1
while choices[index]!=" ":
userchoice = int(input ("That space is taken. Where would you like to put your X?"))
index = userchoice-1
choices[index]="X"
board(choices)
if checkwin(choices)==False:
userchoice = int(input ("Where would you like to put your O?"))
index = userchoice-1
while choices[index]!=" ":
userchoice = int(input ("That space is taken. Where would you like to put your O?"))
index = userchoice-1
choices[index]="O"
checkwin(choices)
return choices
def checkwin (c):
if checkwin1(c)==False:
if checkwin2 (c)==False:
return False
else:
endgame("Player 2")
else:
endgame("Player 1")
def checkwin1(c):
return ((c[0]=="X" and c[1]=="X" and c[2]=="X") or
(c[3]=="X"and c[4]=="X" and c[5] =="X") or
(c[6]=="X"and c[7]=="X" and c[8] =="X") or
(c[0]=="X"and c[3]=="X" and c[6] =="X" )or
(c[1]=="X"and c[4]=="X" and c[7] =="X") or
(c[2]=="X"and c[5]=="X" and c[8] =="X") or
(c[0]=="X"and c[4]=="X" and c[8] =="X") or
(c[6]=="X"and c[4]=="X" and c[2] =="X"))
def checkwin2(c):
return ((c[0]=="O" and c[1]=="O" and c[2]=="O") or
(c[3]=="O"and c[4]=="O" and c[5] =="O") or
(c[6]=="O"and c[7]=="O" and c[8] =="O") or
(c[0]=="O"and c[3]=="O" and c[6] =="O" )or
(c[1]=="O"and c[4]=="O" and c[7] =="O") or
(c[2]=="O"and c[5]=="O" and c[8] =="O") or
(c[0]=="O"and c[4]=="O" and c[8] =="O") or
(c[6]=="O"and c[4]=="O" and c[2] =="O"))
start()
Let's imagine that X wins (because this is probably what caused it to "end") three times.
You then get to if checkwin(choices)==False: within getchoice(),
'checkwin()' happily calls endgame(). endgame() returns, and then getchoice() continues executing.
At the end of getchoice(), checkwin() is called again, with the same result.
After getchoice() returns we get back to while checkwin(choices)==False: within start(), once again with the same result.
Also note that you will see this happening even more times if you actually played multiple games in a row.
Also try making O win, I think you will only have to say no to your prompt twice in that case.
EDIT:
class TicTacToeGame:
INCOMPLETE = 0
WINNER_PLAYER_1 = 1
WINNER_PLAYER_2 = 2
DRAW = 3
SYMBOLS=["X","O"," "]
def __init__(self,player_1, player_2):
self.board = [[-1]*3,[-1]*3,[-1]*3]
self.players = [player_1,player_2]
self.current_turn = 0;
def advance(self):
if self.calcGameState() != TicTacToeGame.INCOMPLETE:
return
while True:
x,y = self.players[self.current_turn].getMove();
if x < 0 or x > 2 or y < 0 or y>2 :
continue
if self.board[y][x] == -1:
break
self.board[y][x] = self.current_turn
self.current_turn += 1
self.current_turn %= 2
def stringify(self):
re = ""
fr = True
for row in self.board:
if fr:
fr=False
else:
re+= "\n" + "---+" * 2 + "---\n"
fe = True
for el in row :
if fe:
fe = False;
else:
re += "|"
re += " " + TicTacToeGame.SYMBOLS[el] + " "
return re
def calcGameState(self):
for i in range(0,3):
#col
if all(self.board[i][j] == self.board[i][0] for j in range(0,3)) and self.board[i][0] != -1:
return self.board[i][0] + 1
#row
if all(self.board[j][i] == self.board[0][i] for j in range(0,3)) and self.board[0][i] != -1:
return self.board[0][i] + 1
if all(self.board[i][i] == self.board[0][0] for i in range(0,3)) and self.board[0][0] != -1:
return self.board[0][0] + 1
if all(self.board[i][2-i] == self.board[0][2] for i in range(0,3)) and self.board[0][2] != -1:
return self.board[0][2] + 1
if all(self.board[i][j] != -1 for i in range(0,3) for j in range(0,3)):
return TicTacToeGame.DRAW
return TicTacToeGame.INCOMPLETE
def stringResult(self):
res = self.calcGameState()
if res == TicTacToeGame.INCOMPLETE:
return "Incomplete"
if res == TicTacToeGame.DRAW:
return "Draw"
return "Player " + self.SYMBOLS[res-1] + " Won!"
class HumanPlayer:
def __init__(self):
self.board = None
def setGame(self,game):
self.game = game
def getMove(self):
print(self.game.stringify())
print("Current turn is: " + self.game.SYMBOLS[self.game.current_turn])
print("enter row for move")
y = input()
print("enter col for move")
x = input()
return int(x)-1,int(y)-1
def playAgain():
playagain=input("Another game? Y/N\n > ")
playagain = playagain.upper()
if playagain == "N":
print("Thanks, hope to see you again soon.")
return False
return True
while True:
p = HumanPlayer()
t = TicTacToeGame(p,p)
p.setGame(t)
while t.calcGameState() == TicTacToeGame.INCOMPLETE:
t.advance()
print(t.stringResult())
if not playAgain():
break;
So i am trying to make a function out of many functions (i believe it is called recursion, read a post about it earlier on this forum)
When i try to make a number of things into 1 big function so i can call upon it later it doesn't seem to be working but when i take away the "func _hello()" and the "endfunc" from the end, everything seems to be working fine. Can someone please explain this to me. I know the problem is occurring because of the the "Conversion" function but i can't seem to understand why this is happening. Please help, language used here is AutoIt
;;;****Program adds spaces *****
;;;***** the input variable here is $New*****
Global $final
Global $Hexadec
Func _hello()
$DataToBeDecrypted = "55fdaf fdafd"
$2space = $DataToBeDecrypted
$New = $2space
$AddingSpace = StringSplit($New, "")
$Final = ""
If Conversion($AddingSpace[0]) Then
For $Spacing = 1 to $AddingSpace[0] Step 2
$Final = $Final & $AddingSpace[$Spacing] & $AddingSpace[$Spacing+1] & " "
Next
MsgBox(0, "Adding space to the message so it can be converted back to Hex", $Final)
Else
MsgBox(0, "Result", "String does not contain an even number of characters.")
EndIf
Func Conversion($Hexadec)
Return Mod($Hexadec, 2) = 0
EndFunc
;;;***The final value is stored in the $final variable****
;***** Hexadecimals to ASCII*****
;;***Input variable is $HexadecimaltoASCII2******
$HexadecimalToASCII2 =$final
$HexadecimalsToASCII = ChrH($HexadecimalToASCII2)
$Ascii2Hex = Sub($HexadecimalsToASCII)
$v5ar = Chr($HexadecimalsToASCII);char
MsgBox(0,"Hex to ASCII",$HexadecimalsToASCII)
Func ChrH($v8)
Local $v5=""
$A1 = StringSplit($v8, " ")
For $count = 1 To $A1[0]
$v5 &= Chr(Dec($A1[$count]))
Next
Return $v5
endFunc
Func Sub($v8)
Local $v9=""
For $count = 1 To StringLen($v8)
If StringLen(Hex(Asc(StringMid($v8, $count, 1)),2)) = 1 Then
$v9 &= "0" & Hex(Asc(StringMid($v8, $count, 1)))
Else
$v9 &= Hex(Asc(StringMid($v8, $count, 1)),2)
EndIf
If $count <> StringLen($v8) Then $v9 &= " "
Next
Return $v9
endFunc
;*****HEXADECIMAL to ASCII*****
EndFunc
It seems like you never call your Hello() function. To execute a function you have to call it.
Try adding Hello() at the top of the file and it should work.
I am a beginner designing a Lua program in ComputerCraft (Minecraft) that asks the player for their name the first time they use it, and records it. For now, I want a program that detects if the variable firstnameis equal to nil and asks for the name if it is so. If the variable is not equal to nil, it says that you do not need to register. I currently have register()called only after pressing 5from the main menu.
The problem is that each time I assign a string to firstnameupon being prompted, firstnamereturns to nil when the main menu comes up. I even put print(firstname)at the end of the menu to test this.
I have assumed this is due to the parallel function that this all runs in. I run MainMenu() and Controls()in parallel so that I can listen for keyboard input and redstone input at the same time.
How can I keep both functions listening and the menu working, while preserving variables?
Here is the full code:
rednet.open("back") --Opens rednet on the back side of computer
local innerdooropen = false --Stuff to do with the airlock
local outerdooropen = false
function reset() --A handy function for screen reset
term.clear()
term.setCursorPos(1,1)
end
local function register() --This is the registration menu system
if firstname == nil then
print "Welcome to Obsidian Station!"
print "You must register before entering"
print "Please type your first name"
local firstname = read()
if firstname ~= "" then
print("Enter your last name")
local lastname = read()
print("You are now registered "..firstname.." "..lastname)
sleep(3)
rednet.broadcast(firstname.." "..lastname)
elseif firstname == "" then
print "You must register to enter"
shell.run("startup")
end
end
if firstname ~= nil then
print("Registration Not Needed")
sleep(2)
end
end
--Beginning of Section You Don't Have to Read
local function MainMenu()
while true do
term.clear()
term.setCursorPos(1, 1)
if innerdooropen == true then
rs.setOutput("left", false)
end
if outerdooropen == true then
rs.setOutput("right", false)
end
if innerdooropen == false then
rs.setOutput("left", true)
end
if outerdooropen == false then
rs.setOutput("right", true)
end
print "Safety Airlock Control"
print "~~~~~~~~~~~~~~~~~~~~~~"
print "[1] Open Outer Door"
print "[2] Open Inner Door"
print "[3] Close Both Doors"
print ""
print "[4] Open Both Doors - WARNING! DANGEROUS!"
print ""
print "[5] Register"
print(firstname)
input = read()
if input == "2" then
print "Inner Door Open"
outerdooropen = false
sleep "1"
innerdooropen = true
end
if input == "1" then
print "Outer Door Open"
innerdooropen = false
sleep "1"
outerdooropen = true
end
if input == "3" then
print "Both Doors Closed"
innerdooropen = false
outerdooropen = false
end
if input == "5" then
reset()
register()
end
if input == "6" then
print("firstname: "..firstname)
sleep(3)
end
if input == "4" then
term.clear()
term.setCursorPos(1, 1)
print "CONFIRM BOTH DOORS OPEN? [y] [n]"
input = read()
if input == "y" then
print "OPENING AIRLOCK DOORS IN"
sleep "1"
print "10"
sleep "1"
print "9"
sleep "1"
print "8"
sleep "1"
print "7"
sleep "1"
print "6"
sleep "1"
print "5"
sleep "1"
print "4"
sleep "1"
print "3"
sleep "1"
print "2"
sleep "1"
print "1"
sleep "1"
innerdooropen = true
outerdooropen = true
print "DOORS OPEN"
sleep "1"
end
elseif input == "n" then
term.clear()
term.setCursorPos(1, 1)
shell.run("startup")
end
end
end
--end of section you don't have to read
local function Controls()
while true do
local e = os.pullEvent()
if e == "redstone" and rs.getInput("bottom") then
redstone.setOutput ("left", true)
sleep "1"
redstone.setOutput ("right", false)
innerdooropen = true
outerdooropen = false
end
end
end
while true do
parallel.waitForAll(MainMenu,Controls)
end
Initialize firstname outside of the parallel. Put local firstname up at the top of your code, and change local firstname = read() to just firstname = read(), and do the same for last name.
You are creating the variable after you check whether or not it is nil, which is why it always returns nil. Similarly, when the function ends, firstname no longer exists, since it is called and created inside of the function. So it will always return nil.
The top of your code should look like so
rednet.open("back") --Opens rednet on the back side of computer
local innerdooropen = false --Stuff to do with the airlock
local outerdooropen = false
local firstname = ""
local lastname = ""
And the other section should be as follows:
if firstname == nil then
print "Welcome to Obsidian Station!"
print "You must register before entering"
print "Please type your first name"
firstname = read()
if firstname ~= "" then
print("Enter your last name")
lastname = read()
print("You are now registered "..firstname.." "..lastname)
sleep(3)
You should initialize firstname before you check if it is equal to nil.
local firstname = nil; --Move this up here so that you can access it elsewhere.
local lastname = nil; --This is also needed out here.
local function register() --This is the registration menu system
if firstname == nil then
print "Welcome to Obsidian Station!"
print "You must register before entering"
print "Please type your first name"
firstname = read() --Change this to just setting firstname to the read name.
if firstname ~= "" then
print("Enter your last name")
lastname = read()
print("You are now registered "..firstname.." "..lastname)
sleep(3)
rednet.broadcast(firstname.." "..lastname)
elseif firstname == "" then
print "You must register to enter"
shell.run("startup")
end
end
if firstname ~= nil then --Now this will not error as well.
print("Registration Not Needed")
sleep(2)
end
end
This allows for firstname to be accessed elsewhere and not just within that first if statement. You were checking if an uninitialized variable was equal to nil or not.
EDIT: Saw purxiz's answer and realized that you also probably want lastname to be outside of the loop. I fixed it in my code above now.