name: string;
id: number;
stats: number;
energy: number;
};```
i have this at the top of my file
and a useState call ` const [handCards, setHandCards] = useState<CardType[]>([]);`
``` const dealHand = () => {
const newDeckCards = [...deckCards];
const newHandCards = [];
// deal 5 cards
for (let i = 0; i < 5; i++) {
// if no more cards are in drawDeck, transfer all discardCards to drawDeck
if (newDeckCards.length === 0) {
console.log("shuffling discard pile into draw pile");
newDeckCards.push(...discardCards);
setDiscardCards([]);
setDeckCards(newDeckCards);
}
// take cards from deck and put them into our hand
newHandCards.push(newDeckCards.pop());
}
setDeckCards(newDeckCards);
setHandCards(newHandCards); // i get an error here
};```
`Type 'undefined' is not assignable to type 'CardType'.`
i know this is because pop() can potentially return undefined if the array is empty. how do i change my code to stop the error?
#pop() undefined type error
200 messages · Page 1 of 1 (latest)
for (let i = 0; i < 5; i++) {
const card = newDeckCards.pop()
if (!card) {
console.log('shuffling discard pile into draw pile')
newDeckCards.push(...discardCards)
setDiscardCards([])
setDeckCards(newDeckCards)
} else {
newHandCards.push(card)
}
}
Your original code also has logical issue, after checking newDeckCards to be empty, you loop body continues to execute and still pushes an undefined card into hands.
if you're popping the new card and pushing the discarded cards, doesnt that mean you'll be drawing discarded cards?
i don't think this is true
the app is working as intended with this code
newDeckCards gets populated with the content of the discard pile
newDeckCards.push(...discardCards);
yup
thats how deckbuilders like Slay the Spire work
when you run out of cards to draw, your discard pile gets shuffled back into your draw pile
I mean take a closer look at your original code.
After the entire if (newDeckCards.length === 0) {} block, it continues to execute, and newHandCards.push(newDeckCards.pop()) will just push a undefined into newHandCards.
It may not affect how your app works, but it is a logical error.
its not undefined though?
by that point there is a card object in newDeckCards
its intentional
because otherwise only 4 cards would get dealt
a hand is 5 cards
Ah I misread.
on each card being dealt it checks to see if deckCard is empty, so it needs to also deal the card on the iteration where it is empty and repopulates deckCards from the discard pile
i am concerned though that it might be undefined at some point because of the life cycle not working as expected
but so far theres no undefined getting pushed in
typescript doesn't know that
well yea exactly
That's why you shouldn't rely on checking newDeckCards.length to assert if it's undefined
Just do this, check directly what you are going to push to see if it is undefined or not.
typescript signature for pop is T |undefined
yes im aware
it's not smart enough to know about other checks you have done or invariants of your program
pretty sure its gonna give the same error for this code because of what michaels saying
as T- assign the value to a variable and use control flow to check for undefined
- same as 2) but use an assertion function to assert that it's not undefined
yea thats what i ended up doing
const newDeckCards = [...deckCards];
const newHandCards = [];
// deal 5 cards
for (let i = 0; i < 5; i++) {
// if no more cards are in drawDeck, transfer all discardCards to drawDeck
if (newDeckCards.length === 0) {
console.log("shuffling discard pile into draw pile");
newDeckCards.push(...discardCards);
setDiscardCards([]);
setDeckCards(newDeckCards);
}
// take cards from deck and put them into our hand
if (newDeckCards.length > 0) newHandCards.push(newDeckCards.pop());
}
setDeckCards(newDeckCards);
setHandCards(newHandCards as CardType[]);
};```
just did as CardType[]
and i make sure to check if newDeckCards has something inside before popping
@placid dove thats good enough right?
You don't need the last as CardType[]
I generally like to avoid ! and would prefer an assertion that would error in future in case the invariants of my program changed
Declare it upfront instead:
const newHandCards: CardType[] = [];
that is a personal preference for me
that doesnt work @placid dove
still get an error on the pop()
Type 'undefined' is not assignable to type 'CardType'.ts(2```
because of what michael said
one moment I'll write something
I literally gave you the answer like the first message
const card = newDeckCards.pop()
if (card) newHandCards.push(card)
Instead of:
- Using
array.length > 0to assertarray.pop()is notundefined.
You should instead: - Just pop and check the result is
undefinedor not.
const newDeckCards = [...deckCards];
const newHandCards: CardType[] = [];
// deal 5 cards
for (let i = 0; i < 5; i++) {
const card = newDeckCards.pop();
// if no more cards are in drawDeck, transfer all discardCards to drawDeck
if (!card) {
console.log("shuffling discard pile into draw pile");
newDeckCards.push(...discardCards);
setDiscardCards([]);
setDeckCards(newDeckCards);
}
// take cards from deck and put them into our hand
if (card) newHandCards.push(newDeckCards.pop());
}
setDeckCards(newDeckCards);
setHandCards(newHandCards);
};```
const dealHand = () => {
const newDeckCards = [...deckCards];
const newHandCards: CardType[] = [];
// deal 5 cards
for (let i = 0; i < 5; i++) {
// if no more cards are in drawDeck, transfer all discardCards to drawDeck
if (!card) {
console.log("shuffling discard pile into draw pile");
newDeckCards.push(...discardCards);
setDiscardCards([]);
setDeckCards(newDeckCards);
}
// take cards from deck and put them into our hand
const card = newDeckCards.pop();
if (card) newHandCards.push(card);
}
setDeckCards(newDeckCards);
setHandCards(newHandCards);
};
const dealHand = () => {
const newDeckCards = [...deckCards];
const newHandCards: CardType[] = [];
// deal 5 cards
for (let i = 0; i < 5; i++) {
// if no more cards are in drawDeck, transfer all discardCards to drawDeck
if (newDeckCards.length === 0) {
console.log("shuffling discard pile into draw pile");
newDeckCards.push(...discardCards);
setDiscardCards([]);
setDeckCards(newDeckCards);
}
// take cards from deck and put them into our hand
const card = newDeckCards.pop();
if (card) newHandCards.push(card);
}
setDeckCards(newDeckCards);
setHandCards(newHandCards);
};
const arrA: string[] = [];
const arrB: string[] = ["foo"];
arrA.push(arrB.pop()) // :(
// ^^^^^^^^^^
// Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
// Type 'undefined' is not assignable to type 'string'.
function assert(x: boolean, msg: string): asserts x {
if (!x) {
throw new Error(msg)
}
}
const arrC: string[] = ["foo"];
const arrD: string[] = [];
const value = arrC.pop()
// ^? - const value: string | undefined
assert(value !== undefined, "expected string") // will error if in future your invariants change
arrD.push(value) // :)
// ^? - const value: string
there ^^^
For future reference, this is the TS way to do things.
burritos way is cleaner
for this situation
its not necessary to do an assertion
i think
benefit of my solution is that you will get a runtime error if your assumption later provides to be wrong
It's the same, michael's has an extra protection.
whereas with burrito it might silently fail
Tbh I wouldn't write your code that way but eh, if it works it works.
you will have a bug but won't know it
how should i write it
cant i just do a if (!card) console.error
generally you just want to error out when an assumption like this is false
there is no point in continuing your program
so cant i just throw an error
const card = newDeckCards.pop();
assert(card !== undefined, "there should always be a card")
newHandCards.push(card);
why not?
I don't know exactly what your program is trying to do, but it seems to be "transfer 5 cards from deck to hands; if there aren't enough deck cards, transfer discarded cards into deck"
No.
but yea thats the behavior
So why not just do the 5 card check upfront?
Why do the check in every iteration of the loop?
because thats not how STS works
it doesnt deal 5 cards at once
it deals one at a time until the deck is empty
then replenishes the deck
It doesn't matter
why not
i guess i dont get how this is superior to just doing if (!card) throw error
console.error doesn't throw
There are 3 cards in deck, 10 cards in discarded.
These two methods produce the exact same result:
A:
- Deal 3 cards from deck.
- Move discarded to deck.
- Deal 2 cards.
B:
- Move discarded to deck.
- Deal 5 cards.
You can run it through your head.
is there no throw command?
well my assertion function does throw
and has the benefit that you don't need to nest things with control flow in your ordinary code
its just an assertion and then you move on
console.error is just logging with a stack trace
in B, you'd have to place the cards under the current deckCards
but maybe in a video game you want to just let the program continue for as long as it can
even if the state of it is completely unknown once an assumption is wrong
Sure
Concat array in whatever order you need.
the thing is thats just not how STS works
you can see the draw pile replenish
and the number of cards leaving the draw pile one by one
until i gets to 0 then it replenishes
its their game design,i think there is probably a mechanical reason for it
due to relic mechanics
theres a lot of items and cards that affect draw mechanics
and discard / exhaust
It has nothing to do with the game.
It's impossible to observe the difference between A and B.
Because your code runs in one single step, and the result of A and B are identical.
@placid dove ok so the reason i think is because there is a maximum hand size of 10, i havnt written in that logic yet
but lets say the player has 2 cards in drawdeck
and 9 cards in hand
if discard gets shuffled in when you say it should
itll incorrectly empty discard into draw despite max card limit being reached after one card being dealt
not that you could've known that
That's the intermediate state, it doesn't matter, only state before and after your function runs is observable.
what?
no im saying the players turn would start with only 1 card being dealt
and that would be correct
and the correct state would be the discard not having been shuffled into draw
if their max hand size was reached after only dealing 1 card
i just havnt written in that logic
for max hand size
Okay yeah then that would matter.
On a side note, your code also doesn't deal with "what happens even deck + discarded combined still has less than 5 cards"
will it recursively loop currently
But anyways, your original question is solved, I'm sure you can figure out the rest yourself 😄
sure, thank you!
yea i havnt made a card removal mechanic yet but that will have to be addressed
(Actually, even given the constraint that you can only have maximum 10 cards at once, you can still write code that doesn't involve a loop)
(If player already has 8 cards in hand, then you simply only "draw" 2 cards rather than drawing all 5)
that logic is hurting my brain
if player has 9 cards in hand and 2 in draw deck
check if ...
what
if 10 - numCardsInHand > 5 ?
also the code does currently all wrk in one step, but ideally it would show each card being dealt one by one
as an animation
- Start with
drawCount = 5. - Update
drawCountbased on how many player already has in hand and max cards player can have in hand. Eg if player already has 8 cards in hand, and player can at most have 10, then updatedrawCountto2. - Check if deck has enough for
drawCount. If deck doesn't have enough, move discarded into deck. - Simply chop the deck for
drawCountand move to player's hands.
If you are going to eventually make it have animation between steps then you should just start writing it that way.
Refactoring later is not going to be fun.
hmm
i thought surface level stuff like that should go last
i guess theres no animation library that will just take my array and deal them into my hand
That's certainly not a surface level refactor.
You need to completely rip apart your for loop, for example.
how would i even do that
are there libraries that will just take a simple array and animate it for me
One way to do it is to have an action queue, and send 5 "draw 1 card" actions into the queue, and the queue simply runs sequentially one by one.
It dequeues an action and execute it.
i actually found a repo of someone who did a STS clone and they had a action queue now that i think about it
But yeah you should probably start looking into that, it's not going to be a simple refactor.
hmmmmmmmMMMM
that sounds like too much work
im just going to ignore it lol
im just making this for fun
and a portfolio project
its fine if it doesn't function exactly like STS
but idk maybe ill do it cause it sounds interesting too
but what i don't get is how that works with react
oh maybe i just need to setState on every iteration of the for loop lol
instead of pushing into the final array
why not that
anyway thanks
i guess i dont see how an action queue is different from just setting state on every iteration
I don't know enough about React to comment on that, good luck on your project though 😄