Dynamically naming an object in Javascript - javascript

I am trying to create a Blackjack game and am having an issue when it comes to splitting the hands. Ultimately I want to create an associative array that has a total and a status for each split, treating each one as it's own hand object. One of the issues I'm having is with the naming functionality which I am trying to do dynamically so each time the Split Function is called, it creates the name according to the number of times the hand has been split.
A bit of background on what I have created so far; A card object holds the name, suit and value (ie. Queen, Clubs, 10). I have an array called cardsInDeck that hold all the card objects in a deck which is shuffled so each card can be pulled randomly. When a card is pulled from the deck, the value is pushed into an array for calculating the value, and the name and suit are concatenated and added to a string to populate the HTML to show the cards on the page.
function drawOne() {
let card = cardsInDeck.pop();
return card;
}
var handNumber = 0;
var playerHand01 = [];
var p1, p2, d1;
function initialDealOut() {
++handNumber;
p1 = drawOne();
playerHand01.push(p1.value);
++playerCount;
let p1n = p1.name + p1.suit;
let p1c = "images/" + p1n + ".png";
let p1Image = document.getElementById('player01');
p1Image.src = p1c;
}
This is repeated for the dealers first card (d1) and the Player's second card (p2) and works well and good. What I would like to do is have the playerHand01 be part of a Hand object that is in a Player array that holds all the hands, and associate a status ("action", "stand", "bust") to each Hand object.
I am trying to accomplish this with something like the code below:
var player = new Array();
function Hand(total, status) {
this.total = total;
this.status = status;
}
function Split() {
++handNumber;
let handName = "playerHand0" + handNumber;
let handTotal = "playerHandTotal0" + handNumber;
handName = new Hand {
handTotal: 0,
status: "action"
}
}
I am new to programming and I know it can be done, just know that the way I am approaching it isn't quite right.
Any recommendations would be appreciated.

I would probably just use an array to store the hands. They'll be accessible by their index. If you want to give them a name, that could be part of the hand object.
let playerHands = [];
function Split() {
++handNumber;
let newHand = new Hand ({
handName: 'playerHand' + handNumber
handTotal: 0,
status: 'action'
});
playerHands.push(newHand);
}
Alternatively, if you want to use an object (perhaps because i've misunderstood your requirements), it is possible to give an object a property dynamically. There are two different syntaxes that can be used for object properties. Both of the following have the same result:
let myObject = {};
myObject.test = 'hi';
and
let myObject = {};
myObject['test'] = 'hi';
You'll usually use the former, but if you don't know the name until runtime, the latter gives you increased flexibility, because you can access using any string. So for example, the following is perfectly valid:
let player = {};
let handName = 'playerHand0' + handnumber;
player[handName] = new Hand();

This is a little confusing to read and I don't think you've fully thought through what information you need to store.
Since the focus of this question is really how to split a hand IMO, I will show you that in hopes that it will point you in the right direction.
First the hand object. I will use es6, but you can do something similar using the constructor method that you have there.
class Hand {
constructor(total, status){
this.total = total;
this.status = action;
this.cards = [];
}
drawCard(){
this.addCard(drawOne());
}
addCard(card){
this.cards.push(card);
this.updateScore();
}
updateScore(){
// loop through and update the score and status
}
// this is the one you've been waiting for
split(player){
// can only split in certain conditions
if(this.cards.length !== 2 || this.cards[0] !== this.cards[1]){
console.log('cannot split this (?)');
return null;
}
// create the new hand
let additionalHand = new Hand(0, 'active');
additionalHand.addCard(this.cards.pop());
player.hands.push(additionalHand);
// need to draw a new card for each hand
this.drawCard();
additionalHand.drawCard()
//return the new hand in case you want to do something with that.
return additionalHand;
}
}
This may not be exactly what you want, but it should be a good initial guide to help.

I would suggest classes for each of the concepts (Player, Card, Deck, Hand, Game), and at the Game level you would keep an array of active hands, which (after a split), could have a different number than the number of players.
Here is how it could look:
class Card {
constructor(name, suit, value) {
this.name = name+'';
this.suit = suit;
this.value = typeof name === 'number' ? name
: 10 + (name === 'Ace');
}
}
class Player {
constructor(name) {
this.name = name;
}
}
class Hand {
constructor(player, total, status) {
this.player = player; // a back reference to the owning player
this.total = total;
this.status = status;
}
addCard(card) {
this.total += card.value;
}
copy() {
return new Hand(this.player, this.total, this.status);
}
}
class Deck {
constructor() {
this.cards = [];
for (let suit of ['Clubs', 'Hearts', 'Spades', 'Diamonds']) {
for (let name of ['Ace',2,3,4,5,6,7,8,9,10,'Jack','Queen','King']) {
this.cards.push(new Card(name, suit));
}
}
}
shuffle() {
for (let i = this.cards.length; i; i--) {
let j = Math.floor(Math.random() * i);
[this.cards[i - 1], this.cards[j]] = [this.cards[j], this.cards[i - 1]];
}
return this;
}
drawOne() {
return this.cards.pop();
}
}
class Game {
constructor() {
this.players = [];
this.activeHands = [];
this.currentHandId = 0;
this.deck = new Deck().shuffle();
}
addPlayer(player) {
this.players.push(player);
// Create a hand for this player:
this.activeHands.push(new Hand(player, 0, "active"));
}
deal() {
// Draw card and assign to current hand
this.activeHands[this.currentHandId].addCard(this.deck.drawOne());
}
nextHand() {
this.currentHandId = (this.currentHandId + 1) % this.activeHands.length;
}
split() {
// Insert the new, copied hand just after the current one:
this.activeHands.splice(this.currentHandId+1, 0,
this.activeHands[this.currentHandId].copy());
}
bust() {
// remove hand from active hands:
this.activeHands[this.currentHandId].status = "busted";
this.activeHands.splice(this.currentHandId, 1);
}
}
// Start a game
var game = new Game();
game.addPlayer(new Player('Helen'));
game.addPlayer(new Player('John'));
// Deal a card to the first active hand
game.deal();
game.nextHand(); // move to second player
game.deal();
// ...etc.
// When you need to split the current hand
game.split();
// Then deal a card to both hands:
game.deal();
game.nextHand(); // move to second hand of same player
game.deal();
// ..etc.
You would of course need to add more game logic and rules. But this could be a template to extend to your needs.

Related

Using function of an object after grabbing it from array

When I try to grab the object from the array, the type is undefined. Therefore I cannot use a method from the undefined object as it doesn't exist. I am relatively new to JavaScript and I have come straight from Java so the way of retrieving objects is kind of new to me. This is what I currently have.
var fleetAmount = 0;
var fleets = [];
function Fleet(number) {
this.number = number;
this.activities = [];
this.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(this);
}
var getFleet = function(fleetNumber) {
return fleets[fleetAmount - fleetNumber];
}
This is where I try to grab the object and preform the function
const Fl = require(‘fleet.js’);
const fleet = Fl.getFleet(fleetNumber);
fleet.addActivity(activity);
I am also working in Node.js, which is how I am using the require method.
In combination with the answer from #audzzy I changed the getFleet() function so that it would be more efficient. I tested it out and it worked. This is what I used
function getFleet(fleetNumber) {
let result = fleets.filter(function (e) {
return e.number = fleetNumber;
})
return result[0];
}
Thanks for the help! I appreciate it.
you want to create a new fleet object and add it, not "this"..
adding "this" would cause a circular reference, where
this.fleets[i] = this (and all fleets would have the same value)
when calling get fleet, I would check that a fleet was returned from get fleet
in case amount is less than the number you send to getFleet (where according to what you posted: 1 returns the last, 2 returns second to last etc..)..
I hope this explanation makes sense.. anyways, this should work..
var fleets = [];
doStuff();
function doStuff(){
addFleet(1);
addFleet(2);
addFleet(7);
addFleet(3);
// should return null
let fleet1 = getFleetByNumber(5);
// should return the fleet with number 7, and not change the fleet with number 1
let fleet2 = getFleetByNumber(7);
if(fleet2){
fleet2.addActivity("activity");
}
console.log(`fleets: ${JSON.stringify(fleets)} \nfleet1: ${JSON.stringify(fleet1)} \nfleet2: ${JSON.stringify(fleet2)}`);
}
function addFleet(number) {
let fleet = { number: number,
activities: [] };
fleet.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(fleet);
}
function getFleetByNumber(fleetNumber) {
return fleets.find(function (e) {
return e.number == fleetNumber;
});
}
function getFleet(fleetNumber) {
let result = null;
if(fleets.length - fleetNumber >= 0){
result = fleets[fleets.length - fleetNumber];
}
return result;
}

Iterate through An Array To Create New Class (JS)

I'm working on using Classes with JavaScript and am not sure how to move forward. I think what I have below performs the for loop successfully, but I'm stuck on how to console.log the newly created Resources.
My goal is to create cards for them using Bootstrap or Foundation then appending them to the body.
My code below:
var ResourcesArray = ["Blacksmith", "Farm", "Gold", "Herb", "Quarry", "Vineyard", "Wood"]
class Resource {
constructor(name, amount) {
this.name = name;
this.amount = amount
}
add() {
this.amount + 1
}
subtract() {
this.amount - 1
}
trade() {
console.log(this.name)
}
}
function createResourceCards() {
for (i = 0; i < ResourcesArray.length; i++) {
new Resource(ResourcesArray[i], 0)
}
}
Thank you for your help!
The expression new Foo() returns the instance that was just created. Just store it in a variable:
function createResourceCards() {
for (i = 0; i < ResourcesArray.length; i++) {
let res = new Resource(ResourcesArray[i], 0);
console.log(res);
}
}
However you probably want the function to return the new resource cards. For this purpose you should make an array and push them all into the array as suggested in comments on the question. However there is also a shortcut to do this: Array#map(), demonstrated below.
const ResourcesArray = ["Blacksmith", "Farm", "Gold", "Herb", "Quarry", "Vineyard", "Wood"]
class Resource {
constructor(name, amount) {
this.name = name;
this.amount = amount
}
add() {
this.amount + 1
}
subtract() {
this.amount - 1
}
trade() {
console.log(this.name)
}
}
function createResourceCards() {
return ResourcesArray.map(res => new Resource(res, 0));
}
let resources = createResourceCards();
console.log(resources);
Each time you use new Resource() it returns the newly created class so you can either store them in an array, object or map. Object/Map with the name as the key would likely be most useful
let resourceClasses = new Map()
function createResourceCards() {
for (i = 0; i < ResourcesArray.length; i++) {
let newResource = new Resource(ResourcesArray[i], 0)
resourceClasses.set(ResourcesArray[i], newResource);
}
}
Then with this newly created Map you can get the resource classes by their name
resourceClasses.has("Blacksmith");
// Returns true/false if you have a key by this name
resourceClasses.get("Blacksmith");
// This returns the value stored in Blacksmith which is your new Class
EDIT: As #Klaycon mentions you can also use .map() as a useful way to iterate over an array and create a new array based of running logic for each element

How can I change the type of a Javascript object?

I am building a webpage-based text adventure game, like Hitchhiker's Guide to the Galaxy or the Zork series. I save objects in this game, like locations or player data, in localStorage, so that the player can continue their progress.
I am using CircularJSON to stringify my circular references within these objects in order to save them.
However, when these objects are parsed, they are of the default Object type.
This is a problem because the functions in types like Area:
var Area = function (tempdescription, tempinitialDesc, tempobjArr) {
this.isExplored = false;
this.description = tempdescription;
this.objArr = tempobjArr;
this.paths = [];
this.initialDesc = tempinitialDesc;
};
Area.prototype.getIndex = function (tempstr) {
if(thePlayer.playerLocation.objArr.length > 0) {
for(var i = 0; i < thePlayer.playerLocation.objArr.length; i++) {
if(thePlayer.playerLocation.objArr[i].name.indexOf(tempstr) != -1) {
return i;
}
}
}
return -1;
};
or Player:
var Player = function (defaultLocation) {
this.inv = []; // an array of game objects
this.playerLocation = defaultLocation; // the player's current location
this.moveCount = 0;
this.score = 0;
};
Player.prototype.getIndex = function (tempstr) {
if(thePlayer.inv.length > 0) {
for(var i = 0; i < thePlayer.inv.length; i++) {
if(thePlayer.inv[i].name.indexOf(tempstr) != -1) {
return i;
}
}
}
return -1;
};
which were created by me, need to be present in each object order for my other code to work.
I need to change the type of several objects in a simple way, if it exists, because when I save these objects there will probably be several dozen of them by the time the game is done.
Is there any way to change the type of a Javascript object?
Option #1 (using ES2015): const newObject = Object.setPrototypeOf(oldObject, Player.prototype)
This is pretty slow though, check the documentation:
Warning: Changing the [[Prototype]] of an object is, by the nature of how modern JavaScript engines optimize property accesses, a very slow operation, in every browser and JavaScript engine.
That's an alternative to...
Option #2 (deprecated): oldObject.__proto__ = Player.prototype
Option #3: const newObject = new Person(/* Use the properties of oldObject */)

Passing Object's Instance in Javascript

Here is what I'm trying to do. I'm trying to pass an instance of order to bill, where it would be indexed. The thing is that it's not working.
Am I stretching JS too thin here?
Any example on how to do this, or some reading material?
EDIT: Maybe I should add that this is supposed to be the user interface for a POS (Point of Sale) system. It should accept the order of products (each one with variable quantity), and process in the client's side the subtotal, total and number of items in the bill.
EDIT2: Not native english speaker. Maybe the names that I choose did not best suited the problem.
function Bill (prefix,maxForms,minForms) {
this.prefix = prefix;
this.maxForms = maxForms;
this.minForms = minForms;
this.items = [];
this.total = 0;
this.addOrder = function(order) {
if (this.items.length == 0)
{
this.items.push(order);
}
for (i=0;i<this.items.length;i++){
if (this.items[i].name === order.name) {
this.items[i].quantity = order.quantity;
this.items[i].price = order.price;
}
else {
this.items.push(order);
}
this.total = this.total + order.getSubTotal();
}
}
}
function Order (name,price,quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
this.getSubtotal = function () {
return this.price*this.quantity;
}
this.changeQuantity = function (newQuantity) {
this.quantity = newQuantity;
}
this.incrementQuantity = function () {
this.quantity = this.quantity + 1;
}
}
Here's an issue:
for (i = 0;/*...*/)
I would suggest you spend a little more time in JS.
It does look a lot like C / Java / C# / PHP, etc...
The problem, however, is that JS does not have any notion of block scope*.
* until ES6, that is
It only deals with function scope.
That is, a variable has the same reference through the whole function where it's defined (via var).
If a variable is not defined via var, the function goes up to its parent to find the value of the variable, and up from there, and up from there, until it hits window.<varname>.
You might actually be modifying window.i in your class' instance.
function Bill ( ) {
var bill = this,
i = 0;
for (i=0; /* ... */) { /*...*/ }
}
That said, you might do to spend time getting to know JS.
Most of what you've written looks absolutely fine, in English, as well.
I might break it down a little further:
function Bill () {
var bill = this;
extend(bill, {
total : 0,
items : [],
addOrder : function (order) {
var match = bill.findOrder(order.name);
if (!match) { bill.items.push(order); }
else { bill.updateOrder(match, order); }
bill.updateTotal();
},
findOrder : function (name) {
var matches = bill.items.filter(function (order) {
return order.name === name;
});
return matches[0];
},
updateOrder : function (current, updated) {
/* I don't know if you want to replace the old order, or add to it... */
/* so I'm "replacing" it, instead of increasing quantity, like you did */
current.quantity = updated.quantity;
current.price = updated.price;
},
updateTotal : function () {
bill.total = bill.items
.map(function (order) { return order.getSubtotal(); })
.reduce(function (tally, price) { return tally + price; }, 0);
}
});
}
var bill = new Bill();
bill.addOrder(new Order(/*...*/));
I'm doing a few things differently, here.
First, extend isn't a "built-in" function; there are a lot of implementations, in all sorts of libraries, but basically, it just saves me from writing bill.x = x; bill.y = y; bill.z = z;..., and use an object, instead.
Next, I'm using var bill = this;
and bill.method = function () { bill.total = /*...*/; };
instead of this.method = function () { };, because once you go two levels down, in functions, this no longer means the object you think it does.
this.method = function () {
this.async(function (response) {
// unless you change it yourself, `this` probably means `window`
this.value = response; // oops
});
};
// instead, try
var thing = this;
thing.method = function () {
thing.async(function (response) {
thing.value = response;
});
};
Of course, you can always mix and match, as long as you know how far down you can go (one level)...
...but that means you really, really need to care about using this a whole lot.
var thing = this;
this.method = function () {
this.async(function (val) {
thing.value = val;
});
};
Much more confusing than just referring to the instance by a variable, rather than combining the two.
There are dozens of ways of doing this; some look very class-like, others might be 100% functional, and in ES6, you might just use classes altogether.
But there are some ideas, and some reasons behind doing them that way (especially if you don't know where the differences are in JS vs the other C-looking languages).
I don't think you're stretching JS too thin, at all.
Once all of the issues on line 80 are fixed. All you need to do is:
var order = new Order("My Order", 12, 2);
var bill = new Bill(blah, blah, blah);
bill.addOrder(order);
A few issues right off the bat:
this.total = this.total + order.subTotal();·
There is a garbage char at the end.
Order does not have a subtotal function. It should be getSubtotal.
The 2 assignments to this.items[i].quantity and this.items[i].price are superfluous, since you are assigning properties to themselves. Remember, this.items[i] === order. This is not a bug, but it is inefficient.
You should have something like this.total = 0; at the top of Bill.
I think you want:
this.items[i].quantity += order.quantity;
this.items[i].price += order.price;
This will update quantity with whatever quantity order has. Secondly, I see you have an order function. Not an order object. Was that intentional? Are you planning to add instances of this bill/order object to each other? I don't think that's where you were going. Make sure they are separate objects that you are nesting.
Are you getting anything except undefined? I don't think you are because you're not returning anything.
Put:
return this;
at the end of your functions. Make sure you save them to a var when you make them:
bill = Bill(v,v,v);
order = Order(v,v,v);
then you can:
bill.addOrder(order);
See if that helps.

Needing some visitor-like design pattern

I will give you a sample example of my problem to remove the logical complexity and let you be focus on the important part. Of course, this example will be a bit useless...
I have a tree structure where node are like that
{
path: "...",
childs : []
}
Now, I have to write all the full paths from root to each leaf in an array.
My design is very poor:
function listPaths(node) {
var result = [];
function listForNode(n, parentFullPath) {
var thisPath = parentFullPath + "/" + n.path;
result.push(thisPath);
n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
listForNode(node, "");
return result;
}
It could be nice but I can't write the test with Mocha without having an insane 600 line code test file. At this moment, you should be asking why. The reason is the complexity of the real purpose, that's not relevant for my question. My goal is to having something 'mockable' cause I'm used to. (Java dev). But I fail.
Do you have any pattern that I can use to resolve this one? I'm not really good at JS patterns. :/
Visitor? Making an Y Combinator? So many possibility...
Thank you for reading me
You need to remember that functions are first class citizens in javascript.
I see that essentially what you have is something like
function createVisitor(parentsAccumulatorInitialValue, parentsAccumulator){
var visitor = function myVisitor (node) {
var result;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
result.push(thisPath);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
result = [];
listForNode(node, parentsAccumulatorInitialValue());
return result;
}
return visitor;
}
var listPaths = createVisitor(
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
But that's not the only abstraction you could take care of:
function createVisitor2(
totalAccumulatorInitialValue,
totalAccumulator,
parentsAccumulatorInitialValue,
parentsAccumulator){
var visitor = function myVisitor (node) {
var total;
function listForNode(n, parentsAcc) {
var thisPath = parentsAccumulator(parentsAcc, n);
total = totalAccumulator(total, thisPath, n);
n.childs && n.childs.forEach(function (child) {
listForNode(child, thisPath);
});
}
total = totalAccumulatorInitialValue();
listForNode(node, parentsAccumulatorInitialValue());
return total;
}
return visitor;
}
var listPaths2 = createVisitor2(
function totalInit() {
return [];
},
function totalAcc(total, thisPath, n){
total.push(thisPath);
return total;
},
function parentInit () {
return "";
},
function parentAcc (parentFullPath, n) {
return parentFullPath + "/" + n.path;
});
Which might be pretty reasonable, but as you can see, I'm already beginning to have trouble finding appropriate names for these variables. In fact, I'd say the name of our function is bad, as doesn't create anything strictly like a visitor object I know of. However, it does work (BTW, I've slightly modified it to handle nulls as well as empty arrays):
> listPaths( { path:"foo",
childs: [{path:"bar", childs: null}, {path:"bob", childs: null}]})
["/foo", "/foo/bar", "/foo/bob"]
It can be modified even further so that your trees don't strictly even have the same structure... but we're already at 4 parameters, which isn't great. It'd be better if your visitor creator were passed a single extensible object with all the necessary methods or values. For instance, maybe (pseudocode):
function createVisitor3(opts) {
//assume we've defined GetDefaults() somewhere local to createVisitor3
// as well as assume that extend is defined somewhere that copies properties
// into a new object like various previously existing libraries do.
opts = extend({}, GetDefaults(), opts);
var totalAccumulatorInitialValue = opts.totalAccumulatorInitialValue;
var totalAccumulator = opts.totalAccumulator;
var parentsAccumulatorInitialValue = opts.parentsAccumulatorInitialValue;
var parentsAccumulator = opts.parentsAccumulator;
var childrenGetter = opts.childrenGetter;
/// etc.
...
}

Categories

Resources