I’m still alive. Still (knock on wood) haven’t caught COVID. Of course, that may have more to do with the fact that I’ve adopted the lifestyle of a bloody hermit than anything else.
So what have I been up to? Quite a lot in the last almost three years. Left one job for another. Haven’t done much coding of late, which makes me sad. There’s always so much stuff to do, you know?
And despite always having stuff to do, I had an idea last night that I thought about more today. Parts of it are a brilliant idea and I want to bring it to life. But parts of it are a terrible, awful idea, so maybe I shouldn’t.
Thinking about this idea, though… It’s in my head. It could be great. But it could be awful.
And it’s gotten me thinking about the past. Specifically, I’ve been thinking about how temporary everything is. How temporary everyone is, largely.
At the turn of the century (and yes, that means 1999 to 2000. Horrifying, isn’t it?), I was in near-constant contact with a bunch of awesome people I’d met online. Thinking back over those times today, I was shocked to realize I can’t name them all. Like, not even close to all of them. Maybe half?
People are just gone from my brain. People whose names would show up in my inbox regularly, just gone. A name comes to me out of nowhere and I have to ask myself if I’m making that up or if that’s a real memory. An email address comes up and I recognize it… and realize I have no idea who it belonged to, while I can decipher another email address to remind me of the names of this person’s kids.
Then, there are certain people from back then who I’ll always remember, though I don’t really know them anymore.
Both are a kind of loss and I don’t know which makes me sadder — that there are people out there with whom I used to communicate regularly whom I’ve almost entirely forgotten (did I make up that name? Did I remember? Oh, whose email address was that?), or that there are people out there whom I knew well, and don’t know at all any longer.
A lot of things can happen in 23 years. While I never looked at the future as something which is set in stone, I don’t think I ever imagined my life as it is today back then. I don’t think I ever imagined forgetting whole peoples’ existences. I don’t think I ever imagined not having certain folks in my life.
And that’s the folly of youth, isn’t it? When we’re young, things are unchangeable, unshakeable, everything will be as it is for the rest of time. We know everything (or we think we do) and we radiate confidence that life is this, whatever this is. We think we’ve figured it out when we’ve barely even begun.
I’ve always been reticent when it comes to change. (Okay, that’s putting it extremely mildly.) I still don’t like it. And I especially don’t like it when I look back and see such major differences between then and now. Even the last couple of years are filled with change. They’re good changes that have happened, but there’s still change.
There will always be change.
And I will probably always be very cranky about change, even when I’m grateful for it.
Still, it’s always so very strange to see so much of it when you think about the past the way I have been. How could whole, actual, full people just be missing from my brain? How can it be that I’m not in touch with others? Younger me is surely upset over it all. Present me is less upset, but still bewildered that nearly a quarter of a century has passed since those days. How is that even possible?
Somewhat deep and rambling thoughts. Welcome to my brain.
(PS: The trivia game stuff in the last entry worked out extremely well for that online community I belong to. In case you were wondering.)
I eventually solved the problem I outlined in my previous post, though I’m still not fully satisfied with it. I wanted a really kind of random ordering to my questions but because MySQL returns them in ascending primary key order (which, in this case, is the question_id), there’s no good way to consistently serve them up in the same random order. So, fine, okay. The questions are still randomly selected, more or less, and then displayed in order according to question_id. Fine. I can live with that, even if I don’t love it.
So I got that working, uh, April … 19? So like, the day after my last entry. It wasn’t particularly hard so much as it was annoying. And it’s still somewhat annoying. But I’ll live.
What hung me up for a full week was what came next:
What to reward the player with after they pass the challenge?
For real, I was stumped.
So I thought about it. A lot. And I watched a lot of YouTube videos of game designers talking about things. They talked about being generous, they talked about player psychology, they talked about extrinsic and intrinsic motivation…
I finally did a real brainstorm. Like an honest-to-god, old-school brainstorm, the likes of which I rarely have done outside of elementary school, where I just wrote down everything I could think of on various pieces of paper. And this was the result.
Then, as if things weren’t tough enough, I also wanted a graduated reward system.
Let’s be honest, I’m asking people to answer 10 questions correctly in 60 seconds — including spelling. Even if you’re picking a category you’re super familiar with, you may not get all ten. In my testing, I usually got around 8/10 for most categories and I wrote the damn questions.
So I wanted to balance for the likelihood that people who pass the challenge get 6-8 questions right and if they get 9 or even 10, they should get something awesome.
Here’s how that turned out.
5 questions or fewer correct:BZZT! Sorry, please try again tomorrow.
6 questions correct:
Visit from the Wise Water Spirit to encourage them
Replenishment of energy
The ability to continue down the river
7 or 8 questions correct:
All of the above, plus…
Moving a distance down the river, courtesy of the Wise Water Spirit
9 questions correct:
All of the above, plus…
Three Fire Opals. I won’t go into what these do specifically, but they’ll be a form of currency.
10 questions correct:
All of the above, plus…
A companion for your travels!
Listen, I was heavily influenced by the BBS games of my youth, okay? When I thought about my favourite moments from Legend of the Red Dragon, I thought about getting a horse that would extend your day. So I decided to incorporate a (temporary) companion for your travels if you manage to get ten questions right. The companion is a large turtle (tortoise? I’m calling it a turtle.) and will make your journey down the river more expeditious, shall we say. That said, at maintenance, the turtle can decide to leave you and go on his merry way. There will be a maximum number of days the turtle will accompany you, too.
The most fun bit is that, back in Grade 8, me and three of the girls in my class at school decided to get turtles and we’d take care of them in the school’s bio lab. My friend, V, named hers Tiny, I named mine Wriggles, another girl, J, named hers Hugo, and then F brought one of her turtles from home whose name was Ribbit.
There’s a very long story here in which J’s friend (and my future best friend, but I definitely didn’t like her at first), nearly killed all the turtles by mistake, but, frankly, that’s a story for another time…
Anyway, I decided that if I was adding a turtle, by golly, that turtle will have a name and there’s a random chance that its name will be either Tiny, Wriggles, Hugo or Ribbit. I’m ridiculously pleased by this. Not only am I adding something cool, but it’s meaningful to me personally because of the names and because of the concept coming from the coolest event in that old BBS game.
So I spent a lot of time modifying the database to ensure that it could handle all the things I needed to add — turtle stuff, Fire Opal stuff… Then, I had to start writing functions to ensure that the database changes happen based on the score you get. So I got that bit done tonight and then I got to start writing the encounter with the Wise Water Spirit, depending on your score, and did that tonight, too.
My code works, my text works (I used switch statements both for the code and for the text) and really, all that’s left is:
make sure you can’t do the rapids again once you’ve done them
add tasks to maintenance, such as turtle stuff
make sure I offer the right options to players based on them passing or failing the challenge
make sure I edit things to incorporate the possibility of a turtle and/or Fire Opals
Once I do all that (mayyyyybe on the weekend???), then I’ll be in good shape to actually launch the challenge. It’s gonna take a couple of hours to launch, for sure, because I have a lot of things to do to the database. Everything from adding category ID numbers (and adding 100 questions about Survivor!) to all the challenge-related tables. So much to do there, so there will certainly be some maintenance first, but we’re definitely getting close to the launch of the first challenge.
I suppose I should really start thinking about the second challenge… Oh boy.
Anyway, I should get some sleep. Hope you and yours are staying safe during this time!
By which I mean my work on Challenge 1: The Rapids, is going very slowly. The rapids are, by nature, quite quick…
So the goal here is to present players with a challenge that they must pass in order to continue on their way down the river. My challenge of choice is for them to pick one of five categories, which are randomly selected. Then, once they pick a category they want, they get ten randomly selected questions from that category.
In theory, all of this works beautifully.
It’s when you want to prevent cheating that things go to hell.
If I could count on no one hitting the back button or no one reloading, then things would be fine. But because I’m not in an app, I can’t control people’s actions, so I have to account for the possibility of rampant cheating.
So I’m locking the category choices. Once shown to the user, they get saved to the database. And once you choose one of them, that’s also saved. And then the ten questions that get randomly selected? Also saved. (And then, because I’m not an utter monster, they will all get wiped at maintenance if you fail the challenge, so that if you had a bad day, you can come back fresh the next day. And this also prevents cheating because you can’t then go look up the answer.)
I’m taking the ten question IDs and storing them, then retrieving them.
The problem is that I’m then retrieving the question details (question, answer, alt answer, etc) from those IDs and they are all coming back in ascending numerical order. This means that when you first look at the questions, they’re in the order in which they were generated and passed back to that page. So, basically:
CHALLENGE PAGE: “Yo, Function, get me ten questions from Star Trek: The Next Generation!”
FUNCTION: “You got it! Okay, get all the questions from Star Trek: The Next Generation. Then, shuffle them. Then, slice off the last ten of them. Then shuffle them again, for funsies. Yo! Challenge page! Coming back at you!”
CHALLENGE PAGE: “Thanks, Function!”
So all of that works. But if these values are locked, this is what happens:
CHALLENGE PAGE: “Yo, Function! Get me the the saved question IDs for this user!”
FUNCTION: “Sure thing! Here you go, there’s ten of them!”
CHALLENGE PAGE: “Awesome, thanks! Now, can you get me the questions for these ten question IDs?”
FUNCTION: “Yep! One sec! Okay, get me all the data for any question matching these ten question IDs! Great. Yo, Challenge Page! Here we go!”
CHALLENGE PAGE: “Thanks! … wait. This… this isn’t the same order, is it?”
FUNCTION: “You never said it had to be the same order.”
CHALLENGE PAGE: “That’s implied, jerk.”
FUNCTION: “IS IT CODED THAT WAY? NO? Then just be glad you’re getting that info, buddy.”
CHALLENGE PAGE: “COME ON, seriously?!”
Pulling the question data based on the submitted question IDs pulls the questions in ascending numerical order by question ID. Even if the question IDs weren’t passed along in that order.
So this leaves me with mismatched questions and answers on any potential reload or revisit of the page. Again, if they don’t try to reload or change categories or questions, everything is fine, but if they do that and then attempt to come back to the challenge page? They can’t possibly win because everything is out of order.
I have three potential solutions and I hate them all.
Store the question ID AND the question itself when locking the questions. Why do I hate this? Because it’s lazy and yet still a lot of work.
When locking the question data, order things in ascending numerical order (and also define that precisely when retrieving the data). I hate this because I don’t want my questions in ascending numerical order. I suppose I could do that and then shuffle things. I guess.
Make sure I’m retrieving the question ID with the question data and redo the question ID variables to add to the form data. This is similar to 2 but involves marginally less rewriting of what I already have there, though still would require me to reshuffle things.
On the bright side, I started out playing with my code tonight not knowing what the problem was and now I’ve not only identified it, but I know why it’s happening (vaguely — basically, if you ever pull data from a database and you don’t order it, you’re kind of asking for randomness) and I also have three viable (if unlikable) solutions.
So there’s that.
Once I’ve done that, I then need to calculate/check the answers. So the plan is that if you get 0 through 5 answers correct, you fail and you get to try again the next day, with new category choices and new questions, as previously noted.
If you get six right, you pass. But then I also want to add rewards for getting 7, 8, 9 or 10 questions right.
My game design “skills”, such as they are, basically do not care about the level of my PHP skills. Like, not in the slightest. As such, my PHP skills are having trouble keeping up with my imagination. Every time I want to add something “cool”, part of me (the part that has to figure out the logic to it for coding purposes) yells at my imagination to say SURE YEAH WE NEED A TON MORE COMPLEXITY IN THIS GAME RIGHT NOW, WELL-DONE, JULIE! It’s not fun to war against your creative side and your logical side, but I guess this is my life, now.
What else is my life? Well, there’s kind of a global pandemic sweeping across the earth. I have not left my apartment building in nearly three weeks, but I also work from home, so it’s mostly business as usual here, interestingly. My greatest worries are running out of milk and not being able to run to the corner store and also not being able to book a grocery delivery before my milk runs out.
Work is busy — like Black Friday/Cyber Monday levels of busy — so I haven’t had a ton of time to code, despite the pandemic. All kinds of people are like “PICK UP A NEW SKILL” and I’m like “can I just have a nap, please?”. I’m excessively thankful that I’m not needing to worry about my next paycheque or being able to afford milk and other groceries, so I definitely can’t complain about being busy. I mean, I can, but it makes me sound like an ungrateful wretch when I am, in fact, eternally grateful to not have to worry about things on that front.
All of this to say, I have no idea when the beta of my game will be out, but I hope hope hope 0.4.0a (which will have Challenge 1: The Rapids in it) will be out by the second week of May. Target is roundabout May 10. Maybe. Hopefully. We’ll see. And that same weekend, I’ll revise my own targets for deadlines and stuff.
And now, bed, and more coding tomorrow. I hope you and yours are well during this troubling time, and please don’t feel like you have to learn a language or pick up an instrument. Some days, all we can do is exist and, frankly, that’s okay.
Well, I’m kind of surprised it hasn’t been longer than nearly four months since I sat down to write here! Go me. That feels like progress of some sort. Maybe.
And speaking of progress, there has been very little progress on River of Kurn of late. This is for a number of reasons. First, November is National Novel Writing Month, in which I participate annually. Once again, I managed to finish 50,000 words of a novel in the 30 days of November! And that meant zero coding, because I also helped plan my own high-school reunion (never you mind just how many years we celebrated), plus work was wild. December arrived and work was even wilder, plus those pesky “holiday” things ate up time in terms of thinking, planning, shopping and preparing.
I did make some attempts to code in October, but failed miserably because I got stuck on a totally optional thing. You see, I wanted to build myself a tool to add more questions to the game easily. Of course, adding them isn’t that hard in the first place, but it was infuriating to add the questions to the questions table, then add in rows for those questions in the statistics tables for the blue game as well as the green game. And if there was an issue importing, I’d have to dump the numbers out of the question table and reassign numbers to the questions before attempting again, or I’d go from, say, question 1331 to 1381.
So I was stuck. I’d never coded something to import from a file before. I can do database dumps and backups, but I had never tried to bring data into the database programmatically before. So I got stuck and then I got busy.
I’ve been off work for a full six days now (and have another 10 to go!) and the holidays are now over for me, so when I woke up at 4am today for no apparent reason, I decided to reorganize my plans for my game and I decided to tackle this import issue.
Four hours of testing, testing, testing and testing some more later, I now have an import tool built into the admin section on my game’s website. I mean, it’s not live yet, I’ll do that when I finish adding another 100 questions (about Star Wars this time) and add a couple of other things to the game. I’m going to rework an introduction question, add text to explain the validation link will be sent to the email address you provide, etc. Anyway. I am super jazzed about this, because it makes adding questions so much easier. I literally browse for the CSV file on my computer, upload it and it does everything for me. Victory! (Another victory: managed to renew the SSL cert for the game site and am feeling very proud about this because it was NOT simple.)
In not so great news, my EVGA GTX 1080 Ti video card died on me just over two weeks ago. It lived about 16 months. I saw my holiday plans of gaming and coding vanish before my eyes. While I could always use the integrated graphics to at least use a monitor and actually use my computer, that’s no way to game. I also have a pretty crappy video card that’s been sitting in my old computer, so that’s what’s in my new computer now. I can’t do much in the way of gaming, but I can power a second monitor, so I can at least get some coding done! (As evidenced by my building my import tool.) The other piece of good news is that I was under my manufacturer’s warranty, so I got approved for an RMA (return merchandise authorization) to send EVGA my card and they’ll send me another one. The bad news there is that even though I paid for 1-day shipping, they only attempted delivery on Tuesday, December 24 (and not Monday, December 23), which means that EVGA was closed. Obviously, they were closed on the 25th as well.
Thankfully, the delivery was accepted on the 26th. But now we’re probably looking at them shipping a replacement next week. And, well, their site says “shipping for online/RMA orders will stop completely on Tuesday, 12/31/2019 at 2pm Pacific Time” and will resume the following Tuesday. So if they don’t ship it out today (Friday) or Monday, I probably won’t get my video card until, uh, mid-late January. Even if they ship it today or Monday, I probably won’t get it before the end of my vacation time. This is the universe’s way to tell me to catch up on TV and coding, I guess.
So that brings me to my plans for 2020 for River of Kurn:
0.2.0 release scheduled for January 4, consisting of: 200+ more questions, the tool I created, other bits and bobs and hopefully a way to keep sessions active or allow them to fail gracefully.
0.3.0 release scheduled for January 25, consisting of making things work properly on mobile.
Beta release scheduled for February 29, consisting of 37 separate issues… hahaha oh boy.
Full release scheduled for July 1, consisting of an additional 15 issues.
And, of course, since adding questions are so much easier now, I’m hoping to add more questions much more regularly.
So that’s what’s up with me. Hoping to post more frequently here, too!
It’s officially the last day of June of 2019 and I haven’t posted here since December 1, 2018.
Whoopsiedoodle.
It’s not to say I haven’t coded over the last seven months, but let’s just say that I have not been coding regularly. I kind of did, for a bit in February and March, but then barely at all in April or May, and not at all in January.
Long story short, the company I work at does an annual company-wide trip, since we’re a distributed team, and I learned… in early January, I think, that we were going to Scotland. Well, genealogy is a bit of a hobby of mine and I am 3/8 Scottish, with one of my dad’s paternal grandparents being Scottish and both of my dad’s maternal grandparents being Scottish. Now, when I’d first been to Scotland with my dad, we went to Cluny, Aberdeenshire, which is where his paternal grandmother is from. So I went all out on the family research in January on my dad’s maternal grandparents, who were both born and raised in Edinburgh before coming to Canada. I don’t want to discuss how much money I spent at ScotlandsPeople! By the time I went to Scotland, I went from knowing just the names of my great-grandparents to having identified all sixteen (or close to it) of my 4x-great-grandparents on that side of the family, mostly including dates of birth, dates of death, and many marriage dates.
Not only that, I had compiled a list of almost 50 addresses where various family members had lived in and around Edinburgh.
Plus, I had nine gravesites spread out across four separate cemeteries to visit, none of which were marked, except the one in freaking Kelso, down in the Borders area. How could I visit Scotland and not visit the one graveyard that had ancestors named on it? Answer, I couldn’t.
And here’s one of my favourite pictures from Edinburgh:
So April was filled with travel and then recovering from travel, then May was upon us and I had a wedding to go to in Virginia, which included spending like 30 hours there but it took me 20 hours there and back on a train from New Jersey, plus the flight to and from Newark. I left my place just before five in the morning on the Friday and only arrived at the hotel in Virginia past 1am on the Saturday. Let me tell you that I quite enjoy the train, but 9+ hours in a row on a train is… not ideal.
And that’s how we get to June.
In terms of coding, the crappy thing is that I’m stuck in my game. There is a legitimate game-breaking bug, but when I try to fix that, I break user sessions. It’s been super discouraging so that’s probably why I haven’t touched anything in June. We’ll see about playing with things this weekend, though.
I keep saying this, but I want an alpha out soon. Like, I wanted it out this weekend. But that was back in May.
Now aiming for around Labour Day. I think that’s doable. That’s two months. If I spend even just an hour a day, that’s 62 hours of work that I could accomplish on the game. Now, to be fair, I won’t spend an hour a day every day, but I think it’s absolutely possible to do at least 50 hours of work between now and Labour Day, even if that means some marathon weekends or chunks of four hours in a single night.
Well, I took a really, really long break from #100daysofcode. Like, 31 days. And it was all because of National Novel Writing Month. For the 8th time ever (and seventh time in a row), I achieved the goal of writing 50,000 words in the 30 days of November. NaNoWriMo is something I have attempted sixteen times. I started in 2002 and I skipped 2011, but I’ve at least attempted it 16 times.
Finally, this year, I’ve pulled even. 16 attempts, 8 victories, 8 losses. And, in the process, I crossed the 500,000 lifetime word mark. Solely during November, over these last 16 years, I have written 500,000 words of various novels. It’s astounding to me. That doesn’t count my other writings, it doesn’t count how I’ve continued on with some of these novels. It’s literally just 480 days of writing. That’s just over 1000 words each and every one of those days. Some days, of course, I’ve written as many as 17,000 words (for real) or as few as 0, but the average is about 1000.
So another November comes to an end and another December begins.
And now, I can go back to coding.
I had figured I could absolutely write for NaNoWriMo and code for 100 days of code at the same time.
Wrong! Just wrong. Maybe I had time, but my brain did not like the shifting back and forth from writing to coding, so I did no coding at all during November. Oh God. What is code?
I’m planning to get back to it tonight or tomorrow. In the meantime, I haven’t been posting here about coding because I’ve been posting about coding in my 100 days of code log on GitHub. I do want to spend more time writing about my challenges and problems here, though, in more long-form writing.
Highlights include my doing a Docker course by Bret Fisher, doing #Hacktoberfest, and generally getting through portions of my game. I’m still in the intro in the game, but 90% of the code I’m writing now will be used in the actual game, so… this is good.
That’s about it for now, but that’s what I’ve been up to. :)
I’ve been thinking about it for at least a couple of months and I’ve decided to embark upon #100daysofcode. Why? Because it’s there!
No, truthfully, I’ve wanted to do this since I first saw the tags on various Instagram posts that I’ve seen. The thing that’s been stopping me is that I literally cannot commit to 100 days straight of code. Probably the soonest I could do that is December 1.
So while I’m starting the #100daysofcode challenge today, September 4, there are two major changes I’m making to the rules for myself. The rules state you need to code one hour a day for 100 days and you can only skip one day every two weeks, adding that skipped day to the end of the 100 original days to ensure that you are actually doing 100 days.
The first rule change here is that I am not going to code from September 25 until the 29th (may or may not be inclusive). Why? I have a work trip. I definitely do not think I can get away with sitting in my room coding for an hour a day when the rest of my colleagues are hanging out and chatting and getting to know one another. As such, instead of this challenge ending for me on December 13, it’ll end much closer to December 18. Or possibly later.
The second is that every November, I participate in National Novel Writing Month. That means that I should be striving to hit 1667 words of creative writing during every day of November. It rarely actually turns out like that — I’ll sometimes go several days without writing and then will suddenly write 7,500 words and will catch up. However, the goal is 50,000 words in 30 days and so because of this, I’m giving myself permission to skip coding 1-2 times a week during November.
So that’s an additional 4-8 days, which beings me to December 22 to December 26. Obviously, you have Christmas in there, too, which needs to be accounted for, so here’s my thinking: in the 119 days between today and December 31 (inclusive), my aim is to reach 100 days of coding for at least one hour a day. That’s totally doable. I may even come in ahead of that projection, depending on how well the writing goes in November.
19 days is a bit of a buffer, definitely, but I think this is an achievable, albeit challenging goal. I did think about putting this off until December 1, when I could conceivably actually do the challenge as intended. But what’s the point of putting off something I want to do that will encourage me to do something I should be doing? While I’m a life-long procrastinator, it seemed silly to me to hold off on this challenge for another three months.
I’ll be primarily posting to Instagram, but those posts cross-post automatically to Twitter. Follow me on either: the username for both is juliebugmtl.
And now, to get a cup of tea and start my first hour of coding in this challenge, since it’s 7:14am and I’ve inexplicably been awake since 4:30.
Earlier this year, I made the decision that I needed a new computer. I mean, this decision has been in the making for a while. I bought my last computer from Dell in 2011 and have upgraded it a few times — added RAM, swapped video cards and such. But it takes forever to load programs. Even loading Chrome can make the computer temperamental. Multitasking? Mostly out the window.
The CPU is an Intel i7 930 quad core and, at cpu.userbenchmark.com, it ranks at 330th of 1122 CPUs ranked. I mean, not terrible, right? Still, I can’t do most of what I want to do as well as I’d like to do it. So I decided, with the help of a friend of mine, that I’d not only buy a new computer, but I’d assemble it myself.
I bought all the parts on Tuesday night and they’re starting to arrive today (Thursday).
The new CPU I’m getting is ranked 8th/1122. Just a bit of an upgrade, eh?
I went all-out on this. We’re talking 16GB RAM (expandable to 64), 500GB SSD and 1TB HDD, GeForce GTX 1080Ti graphics card, SLI-compatible motherboard — and I suspect the 1080Ti will drop (eventually) so it won’t be quite so expensive to buy a second one later on, since the 2080 and 2080Ti have been announced. No peripherals, since I’m just going to use the ones I already have. And I also want a new monitor to replace one of my two (and I’d give one of them to my brother). So the new rig will be able to handle video card upgrades, RAM upgrades and such.
My only worry is nuking my CPU by accident, hahaha oh god. Anyway, I should be assembling everything late next week and I’m super excited to be able to open programs like Photoshop while Spotify is open, or open Discord while playing a game with my brother and not have my ENTIRE COMPUTER FREAK OUT. Even opening my writing program, Scrivener, can make everything chug and then I’ll realize that oops, I left Docker open from the last time I was coding and maybe THAT’S the problem.
So, coding.
When last I wrote, I was dealing with registration validation of things like unique usernames and such. I’ve moved from the PHP validation (mostly — still something left there) to JavaScript/form validation. I’ve used regular expressions (oh, regex, I did not miss you) to ensure that usernames are only 5-16 characters and don’t begin with a number. I’ve made sure that if your email1 and email2 fields don’t match that it alerts you to that. I’m currently working on an implementation of password strength based on the zxcvbn library by Dropbox. I’m having trouble with it and, of course, if something is wrong with the JavaScript, the user gets registered anyway because the validation won’t occur and, since I’m only doing uniqueness checks in PHP, it won’t stop “test” from getting registered unless I’ve already registered “test”. (Yes, I’ll have to ensure that JavaScript is running on the user’s browser in order to play the game, good times.)
It’s slow going with JavaScript. It’s definitely the language I’m least familiar with after my web programming diploma. Like my current error is, I’m sure, a variable scope problem, but I’m not really sure how to fix it. Guess I’ll figure it out going forward.
So once the JavaScript validation is done, I want to go back to the PHP checks and look at making sure I have a unique email, which will mean stripping all the dots and plus signs (and everything after a plus sign) from addresses, to ensure that Gmail users, for example, can’t sign up for multiple accounts. In truth, it’s not the end of the world if there are multiple user accounts, but I’m hoping for some measure of cooperation in the game and that would make things too easy for people.
So I figure I need to hold on to two email addresses — the original one and then a unique check one, and run the unique checks on … both? I guess? I still need to figure out the logic there, which I’ve been putting off, if I’m honest, because I haven’t quite thought it all the way through yet.
Also to do:
add validation email with validation link
ensure validation email has opt-out/blacklist link
And then, I think I could move on to login. Which is also terrifying to me, but I’ve ostensibly done it before, in PHP II, so we’ll see where that goes.
From there, I’ll certainly want an account page so people can:
change their email address on file (but not their username)
reset their password
invite a friend…? (This may only be a v2 thing, but we’ll see)
THEN, I might be ready to actually create a flow for people to actually play the game. Part of the issue here is that I’m still working on story. So far, I can pull trivia questions from a database and keep score. This is awesome. It was hard work to get to that point! But there’s this whole story I vaguely have in mind, which is going to mean keeping track of different variables and that will affect how the gameplay works. I also want to build in a maintenance function (one that runs automatically but also one I can run manually if need be) and I want to add in all kinds of admin functions like being able to add, delete and modify users through an admin frontend rather than mess with the database directly. Same with adding, deleting and modifying questions. I also want to code something to back up my databases on command and reset the game.
Still, once I get the login going and allow for a flow for people to get to the game, maybe I’ll send out a few invitations to some friends to try it out, just to see if they can break the (very basic) functionality I have. If they can break it, I’ll have a lot of work to do before I can think about bringing in the actual gameplay I have in mind.
Whew. So that’s my update.
And while I’ve been writing this, I got notification from Intelcom that my delivery consisting of my new:
CPU
SSD
HD
power supply
motherboard
is arriving in the next ~3 hours. If everything that should be delivered today does arrive, I’ll have had deliveries from UPS, Purolator, Canada Post and Intelcom. In a single day. And I feel for Purolator, because they’re deliverying three packages to me, apparently, including my CPU cooler and my RAM. UPS is bringing me my video card, while Canada Post is bringing me the last part of my nephew’s birthday gift, which is a Spider-Man t-shirt.
Anyway, I should get my day started, despite the fact I just want to go back to sleep. I’ll have pictures of stuff next time, no doubt!
(That title, just so you know, should absolutely be read in the voice of the voiceover dude from The Muppet Show‘s Veterinarian’s Hospital.)
In the week and a half or so since my last entry, I’ve been working on my registration branch, trying to do All The Things ™. Primarily, I wanted to check the username for uniqueness (well, a close approximation thereof) and I wanted to check the email address against a list of domains known to be used by spammers. The username check went pretty smoothly, all things told. I’m using this:
function checkUniqueUsername($normalizedUsername) {
echo "
Unique Username Check
";
// Get the database instance
$pdo = database::getInstance()->getConnection();
//Construct the SQL statement and prepare it.
$sql = "SELECT COUNT(username) AS num FROM users WHERE username = :username";
$stmt = $pdo->prepare($sql);
//Bind the provided username to our prepared statement.
$stmt->bindValue(':username', $normalizedUsername);
Basically, this is a function that is called from the registration page that grabs our database connection info and then asks the database to perform a count of usernames on the users table where the username is the bindValue of :username (meaning $normalizedUsername).
In MySQL, this will return a single-column table, where the column is named “num“. And it will say 0 if there are no entries with that queried username and it will say 1 (… or more?) if 1 (… or more?) exists. Then I’ll return false if there are any entries and return true if there aren’t.
Perfectly logical, perfectly reasonable, works just fine.
So imagine my utter shock and consternation when I tried almost identical code to check the domain of the submitted email address and nothing was flipping showing up in my results. Like, absolutely nothing. Dumping out $row with a var_dump($row) returned a boolean false. What on earth? From my Instagram:
You can see in the image that I tested out the query on the MySQL command line and I got 1 row. So why was I getting a boolean false and why wasn’t I getting my 1 count in the row?!
Obviously, the MySQL query, when I typed it out manually on the command line, was fine. And I had logs running, so I could see that the bindValue was working and was fetching the domain in question. So what was the issue?
I took the weekend off because I went to my friend’s wedding in Ontario — I was a bridesmaid — and, rather than sleep like the dead on Sunday night, I coded. I was getting ticked off. Something wasn’t right.
I tried all kinds of things — different bindValue. No bindValue. Different files. Taking the checks out of the functions. Putting them back in. And why on earth was the username check doing just fine but the domain blacklist wasn’t?!?
I finally figured it out on Tuesday evening. I was googling stuff, for the billionth time, and found this issue on GitHub. In it, down a bit, it says:
Running a GRANT command to allow SELECT for this user solved the problem
I froze, the realization crashing over me like a wave. But of course the user has permission, my queries worked… just… fi… I was logged in as root. OH MY GOD, I WAS LOGGED IN AS ROOT.
Fun fact: the user didn’t have permission on the email blacklist table. Just the user table. That’s why everything was empty! That’s why I was getting a boolean false! And I was consistently logging in to the MySQL database as root, which is why those queries worked! I quickly made an edit to my creation SQL file and then manually added permission for my user — voilà. It worked perfectly.
It worked so well that I added the following checks:
email blacklist (the original one I’d been having trouble with)
disallowed usernames (like admin, etc)
voluntary email blacklist (for people who want to opt-out and don’t want to receive invitations to the game)
I still need to compile a list of profane words to prevent people from using them as a username, then add that check.
I also want to add in a unique email check, which will require:
storing the email address entered and use this for any communications
transforming it all to lowercase for uniqueness, so JULIE @ is the same as julie @
stripping periods from the username portion of the email address (julie [at] gmail.com is the same as j.ulie [at] gmail.com is the same as j.u.l.i.e [at] gmail.com) to check for uniqueness
stripping any plus signs and anything that follows up through to the @ sign (julie+test [at] gmail.com, for example, is the same as julie+test2 [at] gmail.com) to check for uniqueness
I also want to implement some JavaScript checks, which, shockingly, I actually know how to do:
Username between 5 and 16 alphanumeric characters in length
Password strength
Email address (1) matches email address (2)
Once all the checks are implemented, I want to send a verification email to the email on file, which ideally also holds a “delete me” link that, when clicked, will trigger insertion of that email address into the voluntary blacklist. Of course, if they click the verification link, this will activate the actual account.
Plus, I probably want to ask permission for whether or not I can add them to a mailing list. That seems useful.
So, I’m feeling pretty awesome for realizing it was a permissions issue. I’m feeling slightly less awesome for having made the mistake of testing with root versus testing with the actual user in question, or having forgotten to give the user the right privileges in the first place, but I still fixed it.
I have made a great amount of progress on my game so far, at least as I see it. But before we get to that — and we will! — I wanted to show off my new living room workspace. I’m fortunate to live in a 4 1/2 (two bedroom) apartment by myself, so I have a spare room. The spare room has been the office for just about a year, now, and that’s where I primarily do, you know, work. My previous remote-working experiences have shown me that I’m way too likely to work too hard and too long if the work area is in my living area, so I keep work and life as separate as possible. (Although this past week, I’ve worked exclusively from my living room because I have an air conditioner in here but not in the office. As a result, I’m pretty sure I worked about 42 or 43 hours this week. Oops?)
And I’m adoring it. Sure, the wires are absolutely revolting, but I’m not going to cable manage anything just yet, because I’m on the cusp of getting a new computer. I’m going to be attempting to assemble it myself from the various component parts. I’m not much of a hardware person (although I’ve installed RAM, a secondary HD, a primary HD and a video card), so my friend, Andrew, has sworn to me that he’ll FaceTime me and walk me through the assembly if I need a hand. So with a new computer imminent, cable-managing right now is dumb. Once that’s all set up, though, you can bet your bippy I’ll be doing my best to hide all those unsightly cables.
So the new workspace is pretty great for the time being and having the monitors mounted to the stand is AMAZING. I’ve reclaimed so much desk space I hadn’t even realized I was missing. I genuinely like sitting here and my HUE lights just make me happy. :)
Okay, so that’s the update on the workspace in my living room. Now, on to the game stuff!
Once upon a time, I used to chat on IRC (Internet Relay Chat) pretty consistently. Like, I’d go to bed for the night, but just leave my computer and IRC client running, and I would always have my IRC channels in the background while I was working. I mean, I was working as an independent contractor while I was the Chatting Online Guide at About.com, sooooo I’d have to say that was pretty reasonable. Anyway, I made friends online, as one does, and several of them were in Australia. Australia has a pretty significant time difference between the East Coast of North America and, well, virtually anywhere in Australia, so getting to chat with the Aussies usually meant someone was up late or up early.
One day, I woke up and stumbled to my desk and blearily tabbed over to mIRC and, in one of my channels, one of the Aussies had been freaking out several hours prior and had eventually gone all-caps in her exclamations and said “WAKE UP I NEED TO ENTHUSE!!!!!!!”
I have had this deep need to enthuse about my game for a couple of days now, and I don’t really have a lot of people in my life who, a) care about this little project of mine, and b) know anything about programming. So that’s why I’m enthusing here, I guess. ;)
Updating my personal site last week is what got me itching to code this week, because I’d played around with self-submitting forms, which is currently how I’m testing out question/answer functionality. So I sat down to try to finish getting PDO to work for me on my test page.
I’d also made a change to my docker environment in that I created three database users that are limited in their permissions — one can edit the users table, one can edit the tokens table and one can read the questions table. So that involved making sure I was using the limited questions user rather than the root user, too.
For some reason, my MySQL PDO command wasn’t asking one question in isolation, which pointed to an issue with my select statement, which, in turn, pointed to an issue with how I was “randomly” selecting a question row. Once that was sorted, my question was being asked! Hooray! Being able to pull in that question from the array felt pretty great. Of course, in order to get to the point where I was able to replicate what I’d done when I was messing around back in January, I was going to need to be able to check the answer and inform the user if they were right or not.
So my first major issue was “how to carry over the user’s answer to the page upon submission?”, but it ended up not being that hard because I remembered I could just grab the $_POST[‘playeranswer’] data and stash that in a variable. I also strtolower() it, because all the database answers are in lowercase, so for a match, the cases need to match, too.
That worked, but if I wanted to also display the last question, along with its correct answer, and the user’s own answer (plus the correct/incorrect validation), I was going to need to pass along the row from the last question. So I pass that along, currently, in a hidden form field so I can snag that from the $_POST[‘lastquestionrow’] field.
When you load the page on a GET, you don’t have any of that, but when you load it on a POST, you now get last question row and player answer stored into their proper variables. Then, the database is called and we start getting into the fun stuff:
hey, here was the row for the last question, please display for me the question and the answer
also here’s what the player said, so show that
if they match, say so and grant the player a point, announce new score
if they don’t match, say so, don’t grant a point, repeat score
if they don’t match but player has no score, say so, don’t grant a point and let the player know they currently have no score
Now, for the most part, this worked just fine. However, I realized, after having done this, why my teachers in my certificate always recommended I go for a does not equal comparison rather than a strict equality comparison. It’s because a strict equality comparison is messed up, yo. I noticed, through my testing, that I was occasionally getting false positives while going through answers that had more than one possible answer and if one of the possible answers has more than one word. So like, what’s the capital of Canada was fine, because that’s Ottawa. But for asking about the capital of the province of Quebec, that’s where issues cropped up. Your choices are “quebec” or “quebec city”. And yet, somehow, with my earlier logic, “city” was acceptable.
In another example, “flash” was an acceptable answer instead of “barry allen” or “the flash”.
And, in the weirdest example, the name “david” was accepted when neither answer was even remotely close to “david”.
So there was something wrong with my comparison logic. At first, I thought it was just that I was using == instead of ===, so I thought okay, stick in a third equals sign there and I’m good! That’s what makes it a strict comparison, right? Right. Should be sorted!
Alas, no, the issues continued, but I went to bed.
The next day, the strange bugginess was in the back of my head all day long. When I sat down to code a bit after work, I really could have rewritten the logic to test for a failed match rather than a positive match, but I was curious as to what in tarnation was going on. How is “david” considered equivalent to one of two completely unrelated names? (Adam and Carter, if you’re curious.)
I sat down and did more robust testing, writing down all the weird results. So I pulled out my Programming PHP O’Reilly book and looked up comparisons, because everything I’d googled was telling me that === should do the trick.
And this is where I learned about the string compare function: strcmp()
The way I understand it is that strcmp() will compare two strings and will return a 0 if they’re equal. So, this then meant I needed to tweak my logic again. A simple “if $string1 === ($string2 || $altString2)” type of statement wasn’t going to work. I needed to funnel the player’s last answer into a use of strcmp() and compare it with the last answer as well as the last alt answer, if the alt answer existed. So here’s what I ended up with.
Basically, that says, here’s the mainVerificationCheck variable, which is comprised of a value returned by using the string compare function. By default, this compares the variable containing the last player answer as well as the last correct answer from the last question. Additionally, if the last question had an alternate answer (like in our Quebec City/Quebec question), create a variable called altVerificationCheck and use the string compare to test the last player answer against the last alternate answer.
Then, if the main check returns a 0 (which means a match) or if the alt check returns a 0, then do stuff. (In this case, it’s notify the user they’re right and their points have increased.)
I’ve been playing around with Instagram stories and captured my bug hunting and fix. Here are the pics from that.
So yeah, I got a lot done over the last couple of days. I worked on stuff on Thursday night and then Friday night well into Saturday morning. And I was going to start in on registration and user creation tonight — my Docker containers are up and running, I have a fresh branch of master that I’ve checked out to a new branch called registration, but I’ve had a headache pretty much all day Saturday and it’s like, four in the morning on Sunday, so I should probably go to bed. I’d have gone to bed way earlier but my headaches are largely sinus-based, so lying down is actually super painful when my sinus cavities are full. Gravity helps the head to drain, I guess!
So my headache meds seem to be kicking in and I probably won’t be able to get back to coding until at least the week of the 6th, because a friend of mine’s getting married on the 4th, and I’m a bridesmaid, so I have a lot of stuff to do this week, then I go to Ontario for the wedding on the weekend. Looking forward to finalizing my computer build either this week or next. I think the new computer will encourage me to do even more coding, and possibly not at ridiculously late hours of the night!
When I started talking about this project, back in January, I was thinking an alpha version might be available in last September.
… yeah, not sure that’s going to happen, hahaha. The base functionality of questions and answers? Sure. But not the game itself.
Anyway, the next task is going to be user registration and login, so I can start saving stuff to the user table and saving stuff to sessions. I want to not have repeat questions, so I’m going to save a certain number of questions in the session (I think?) and check that when doing the random question picker. I think.
Maybe. We’ll see how this goes. I’ll definitely keep you, whoever you are, dear reader, posted on my progress.