How to make an If statement check for if a variable equals a negative number; issue making an adventure game with X,Y coordinates - if-statement

I'm new to Javascript (and coding in general), and I'm trying to make a pretty basic in-browser adventure game, but immediately I've encountered a problem. I wanted it to plot your location as if on an X,Y graph so I could easily mark out a map for it later with coordinates. So, you start at 0,0, and when you move West it changes to -1,0, so on, and I'm using If statements to check your location. But, it always assumes you're at the lowest negative possible, and so always says you're at 'Room 3'.
I think it does this because it doesn't recognise the - in the check as part of the number, and instead thinks you're asking if it equals the number minus itself, which would be zero, which is the game's default position. But, at the same time, it still thinks you're at the lowest possible position even if you move off 0,0, so I don't know. If that is the case, I would like to know how to fix it, and if that isn't the case, I would like to know what the real problem is.
Below is the entire program so far. This is my first time really trying to code something by myself, with no tutorial involved, so I apologise for how training-wheels-y it is.
let x=0
let y=0
let time=0
let button = document.getElementById('button');
let buttonNorth = document.getElementById('buttonNorth');
let buttonWest = document.getElementById('buttonWest');
let buttonEast = document.getElementById('buttonEast');
let buttonSouth = document.getElementById('buttonSouth');
button.onclick = function() {
reporter()
locatron()
}
buttonNorth.onclick = function() {
updateNorth()
reporter()
locatron()
}
buttonWest.onclick = function() {
updateWest()
reporter()
locatron()
}
buttonEast.onclick = function() {
updateEast()
reporter()
locatron()
}
buttonSouth.onclick = function() {
updateSouth()
reporter()
locatron()
}
function reporter() {
console.log(`location is: ${x},${y}`)
console.log(`time is: ${time}`)
}
function updateNorth() {
y = y+1
time=time+1
}
function updateWest() {
x = x-1
time=time+1
}
function updateEast() {
x = x+1
time=time+1
}
function updateSouth() {
y = y-1
time=time+1
}
function locatron() {
if(x==0,y==0) {
totalDescription.innerHTML = `Room 1`
}
if(x==-1,y==0) {
totalDescription.innerHTML = `Room 2`
}
if(x==-2,y==0) {
totalDescription.innerHTML = `Room 3`
}
else {
totalDescription.innerHTML = `This area has not been written yet.`
}
}
I've tried turning the number in the If statement into a string, but it just does the same thing. The only way I can get it to display any message other than 'Room 3' is by moving it along the Y axis, at which point it changes to my equivalent of an error message, 'This area has not been written yet.'. I also tried looking the problem up, but the wording on it is so finnicky that the only results I would get back are how to 'check if a variable is negative', which is not at all what I want.
I appreciate any help I can get.

Related

What's an idiomatic way to traverse and update data structures functionally in Scala?

I'm coming from a Python-heavy background and trying to learn Scala through a basic "Design a Parking Lot" exercise. I have Scala code that looks something like:
class ParkingLot(spaces: Set[ParkingSpace]) {
var openSpaces: Set[ParkingSpace] = spaces;
var usedSpaces: Set[ParkingSpace] = Set()
def assign(vehicle: Vehicle): Boolean = {
var found = false;
for (s <- openSpaces) {
(s.isCompatibleWithVehicle(vehicle)) match {
case true => {
if (!found) {
s.acceptVehicle(vehicle)
openSpaces -= s
usedSpaces += s
found = true
}
}
case _ => {}
}
}
found
}
}
The logic is pretty simple - I have a ParkingLot with Sets of open and occupied ParkingSpaces. I want to define a function assign that takes in a Vehicle, loops through all the openSpaces and if it finds an available space, will update the open and used spaces. I'm having a hard time coming up with a good idiomatic way to do this. Any thoughts and suggestions about how to reframe questions into a Scala mindset?
The main problem with this code is use of mutable state (var). Rather than changing an existing object, functional code creates new, modified objects. So the functional approach is to create a new ParkingLot each time with the appropriate allocation of spaces.
case class ParkingLot(open: Set[ParkingSpace], used: Set[ParkingSpace])
{
def assignVehicle(vehicle: Vehicle): Option[ParkingLot] =
open.find(_.isCompatibleWithVehicle(vehicle)).map { space =>
ParkingLot(open - space, used + space.acceptVehicle(vehicle))
}
}
assignVehicle can return a new parking lot with the spaces appropriately updated. It returns an Option because there might not be a compatible space, in which case it returns None. The caller can take whatever action is necessary in this case.
Note that ParkingSpace now as an acceptVehicle that returns a new ParkingSpace rather than modifying itself.
As also the answer by #Tim mentioned, you need to avoid mutations, and try to handle this kind of state managements in functions. I'm not gonna dive into the details since Tim mentioned some, I'm just proposing a new approach to the implementation, which uses a map of spaces to weather they're used, and returns a new (not optional) instance every time you assign a new vehicle (if the vehicle fits in, updated instance is returned, and if not, the same instance):
class ParkingLot(spaces: Map[ParkingSpace, Boolean]) {
def withVehicleAssigned(vehicle: Vehicle): ParkingLot =
spaces.collectFirst {
case (space, used) if !used && space.isCompatibleWithVehicle(vehicle) =>
new ParkingLot(spaces.updated(space, true))
}.getOrElse(this)
}
Almost the same process goes for removing vehicles, the usage would be something like this:
parkingLot
.withVehicleAssigned(v1)
.withVehicleAssigned(v2)
.withVehicleRemoved(v1)
Since most answers already explained the importance of immutability and creating new objects, I am just going to propose two alternative models and solutions.
1. Using a queue of empty spaces plus a set of used ones.
final case class ParkingLot(freeSpaces: List[ParkingSpace], occupiedSpaces: Set[ParkingSpace]) {
// Returns the used space and the new parking lot.
// An option is used since the parking lot may be full.
def assign(vehicle: Vehicle): Option[(ParkingSpace, ParkingLot)] =
freeSpaces match {
case freeSpace :: remainingSpaces =>
val usedSpace = freeSpace.withVehicle(vehicle)
Some(copy(freeSpaces = remainingSpaces, usedSpaces = usedSpace + usedSpaces))
case Nil =>
None
}
}
2. Using a List[(ParkingSpace, Boolean)] and a tail-recursive function.
final case class ParkingLot(parkingSpaces: List[(ParkingSpace, Boolean)]) {
// Returns the used space and the new parking lot.
// An option is used since the parking lot may be full.
def assign(vehicle: Vehicle): Option[(ParkingSpace, ParkingLot)] = {
#annotation.tailrec
def loop(remaining: List[(ParkingSpace, Boolean)], acc: List[(ParkingSpace, Boolean)]): Option[(ParkingSpace, List[(ParkingSpace, Boolean)])] =
remaining match {
case (parkingSpace, occupied) :: tail =>
if (occupied) loop(remaining = tail, (parkingSpace, occupied) :: acc)
else {
val usedSpace = parkingSpace.withVehicle(vehicle)
val newSpaces = acc reverse_::: ((usedSpace -> true) :: tail)
Some(usedSpace -> newSpaces)
}
case Nil =>
None
}
loop(remaining = parkingSpaces, acc = List.empty).map {
case (usedSpace, newSpaces) =>
usedSpace -> copy(newSpaces)
}
}
}
Note, the boolean may be redundant since the ParkingSpace should be able to tell us if it is empty or not.

Flutter/Dart: How to get list value where key equals

I'm not sure why I'm having such a hard time finding an answer for this, but I have a list that I need to get the value from where the key matches certain criteria. The keys are all unique. In the example below, I want to get the color where the name equals "headache". Result should be "4294930176".
//Example list
String trendName = 'headache';
List trendsList = [{name: fatigue, color: 4284513675}, {name: headache, color: 4294930176}];
//What I'm trying
int trendIndex = trendsList.indexWhere((f) => f.name == trendName);
Color trendColor = Color(int.parse(trendsList[trendIndex].color));
print(trendColor);
Error I get: Class '_InternalLinkedHashMap' has no instance getter 'name'. Any suggestions?
EDIT:
Here's how I'm adding the data to the list, where userDocuments is taken from a Firestore collection:
for (int i = 0; i < userDocument.length; i++) {
var trendColorMap = {
'name': userDocument[i]['name'],
'color': userDocument[i]['color'].toString(),
};
trendsList.add(trendColorMap);
}
I guess, I got what the problem was. You were making a little mistake, and that was, you're trying to call the Map element as an object value.
A HashMap element cannot be called as f.name, it has to be called f['name']. So taking your code as a reference, do this, and you are good to go.
String trendName = 'headache';
List trendsList = [{'name': 'fatigue', 'color': 4284513675}, {'name': headache, 'color': 4294930176}];
//What I'm trying
// You call the name as f['name']
int trendIndex = trendsList.indexWhere((f) => f['name'] == trendName);
print(trendIndex) // Output you will get is 1
Color trendColor = Color(int.parse(trendsList[trendIndex]['color'])); //same with this ['color'] not x.color
print(trendColor);
Check that out, and let me know if that helps you, I am sure it will :)

Google Charts "Uncaught (in promise) Error: Invalid column index 3. Should be an integer in the range [0-3]."

Before you mark this as duplicate, note that others are asking about the error Invalid column index undefined. ... or Invalid column index 5. Should be an integer in the range [0-4]. But no. Mine is "3 should be an integer in the range of [0-3]." Also, the table does work without the formatter.format() line (just no formatting).
google.charts.load('current', {'packages':[data.chartType.toLowerCase()]});
google.charts.setOnLoadCallback(function(){
var googleData = new google.visualization.DataTable();
for (var h in data.headers) {
googleData.addColumn(data.headers[h].type, data.headers[h].html);
if (data.headers[h].format) {
var formatter = new google.visualization.NumberFormat(data.headers[h].format);
console.log(data.headers[h].format);
formatter.format(googleData, h); // Errors Here
}
}
/* ... Add Rows ... Draw Chart ... */
}
The header in question looks like this:
header[3] = {
"html": "Total Amount",
"source": "total_amount",
"type": "number",
"format": {
"negativeColor": "#F05840", //orange
"negativeParens": true,
"pattern": "#,###",
"prefix": "$",
"suffix": "",
}
}
I can't figure out why it would be erroring.
Please forgive me for any typos here, I had to hand-edit the spacing and remove my company's specific info upon pasting the code here.
Edit
WhiteHat is correct in that my h variable was a string instead of an integer, and calling parseInt did remove that error. However, instead of calling parseInt on the formatter and wherever else it's needed, I got rid of my for (var h in data.headers) calls and went with the bog-standard for (var h = 0; h < data.headers.length; h++). Although more verbose with more room for typos, it's far more standardized and predictable.
I'm still having issues with GoogleCharts NumberFormatter, but that's for another round of research and questions, not this one.
make sure you're passing a number (3),
and not a string ('3'),
by using --> parseInt...
e.g.
formatter.format(googleData, parseInt(h)); // <-- here

Tag-it shows no delete-symbol

I don't know why I don't get the "X" symbol ...
It should be like this:
How can I find the problem? Maybe an CSS-file is blocking an other CSS-file?
JavaScript-Code
$(function() {
var sampleTags = ['Klavier', 'Blockflöte', 'Schlagzeug', 'Gesang', 'Saxophon', 'Klarinette', 'Keyboard', 'Panflöte', 'Mundharmonika', 'Beatboxing', 'Akkordeon', 'Cello', 'Bratsche', 'Tuba', 'Kontrabass', 'E-Gitarre', 'E-Bass', 'Akustikgitarre'];
$('#singleFieldTags').tagit({
availableTags: sampleTags,
singleField: true,
singleFieldNode: $('#mySingleField'),
beforeTagAdded: function(evt, ui) {
var counter = jQuery.inArray(ui.tagLabel, sampleTags);
if(counter != -1) {
return true;
} else {
$('.tagit-new input').val('');
return false;
}
},
});
});
Susanne, encountered the same issue. After a significant amount of time wasted, I determined that the 'x' icon is present, it's just not rendering on the browser level.
var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \xd7 is an X
This is line 485 of tag-it.js; when you check the rendering of your page, you'll see this is actually present. I changed it to the following in order to confirm that it was present and just needed to be rendered differently.
var removeTag = $('<a><span>x</span></a>') // \xd7 is an X
It can be styled however, from this point. Hope this helps anyone encountering this issue.

Restrict TextField to act like a numeric stepper

I am making a numeric stepper from scratch, so I want my text field to only accept numbers in this format: xx.x, x.x, x, or xx where x is a number. For example:
Acceptable numbers:
1
22
15.5
3.5
None Acceptable numbers:
213
33.15
4332
1.65
Maybe this will help some how:
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/text/TextField.html#restrict
This is what I got so far:
var tx:TextField = new TextField();
tx.restrict="0-9."; //Maybe there is a regular expression string for this?
tx.type=TextFieldType.INPUT;
tx.border=true;
You can copy past this in flash and it should work.
Thank you very much for your help good sirs.
Very similar to TheDarklins answer, but a little more elegant. And actually renders _tf.restrict obsolete, but I would still recommend using it.
_tf.addEventListener(TextEvent.TEXT_INPUT, _onTextInput_validate);
Both of these event listeners here do the EXACT same function identically. One is written in a one line for those who like smaller code. The other is for those who like to see what's going on line by line.
private function _onTextInput_validate(__e:TextEvent):void
{
if ( !/^\d{1,2}(?:\.(?:\d)?)?$/.test(TextField(__e.currentTarget).text.substring(0, TextField(__e.currentTarget).selectionBeginIndex) + __e.text + TextField(__e.currentTarget).text.substring(TextField(__e.currentTarget).selectionEndIndex)) ) __e.preventDefault();
}
for a more broken down version of the event listener
private function _onTextInput_validate(__e:TextEvent):void
{
var __reg:RegExp;
var __tf:TextField;
var __text:String;
// set the textfield thats causing the event.
__tf = TextField(__e.currentTarget);
// Set the regular expression.
__reg = new RegExp("\\d{1,2}(?:\\.(?:\\d)?)?$");
// or depending on how you like to write it.
__reg = /^\d{1,2}(?:\.(?:\d)?)?$/;
// Set all text before the selection.
__text = __tf.text.substring(0, __tf.selectionBeginIndex);
// Set the text entered.
__text += __e.text;
// Set the text After the selection, since the entered text will replace any selected text that may be entered
__text += __tf.text.substring(__tf.selectionEndIndex);
// If test fails, prevent default
if ( !__reg.test(__text) )
{
__e.preventDefault();
}
}
I have had to allow xx. as a valid response otherwise you would need to type 123 then go back a space and type . for 12.3. That is JUST NOT NICE. So 12. is now technically valid.
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.events.TextEvent;
public class DecimalPlaces extends Sprite
{
public function DecimalPlaces()
{
var tf:TextField = new TextField();
tf.type = TextFieldType.INPUT;
tf.border = true;
tf.width = 200;
tf.height = 16;
tf.x = tf.y = 20;
tf.restrict = ".0-9"
tf.addEventListener(TextEvent.TEXT_INPUT, restrictDecimalPlaces);
addChild(tf);
}
function restrictDecimalPlaces(evt:TextEvent):void
{
var matches:Array = evt.currentTarget.text.match(/\./g);
var allowedDecimalPlaces:uint = 1;
if ((evt.text == "." && matches.length >= 1) ||
(matches.length == 1 && (evt.currentTarget.text.lastIndexOf(".") + allowedDecimalPlaces < evt.currentTarget.text.length)))
evt.preventDefault();
}
}
}