0
\$\begingroup\$

I'm working in C# in Unity on a tetrominoes game. I have seven basic shapes (I, J, L, O, S, T, Z) but each of the four component squares on each shape is labelled with an identifier unique to the shape set. So the shapes include, e.g., an S-1,2,3,4, S-5,6,7,8, S-9,10,11,12, J-13,14,15,16, J-17,18,19,20, Z-21,22,23,24....

Currently, in my Spawner class, I have the following to fetch a shape randomly from the shape array, m_shapeSet1:

public class Spawner : MonoBehaviour {

public Shape[] m_shapeSet1;

Shape GetRandomShape()
{
    int i = Random.Range(0, m_shapeSet1.Length);
    if(m_shapeSet1[i])
    {
        return m_shapeSet1[i];
    }
    else
    {
        return null;
    }
}

I thought it would be simple to use m_shapeSet1.RemoveAt(i) before the final closing brace after each fetch to avoid fetching, and later spawning (there is another function to spawn shapes), a duplicate shape. BUT, Visual Studio responds with: (1) the RemoveAt request is unreachable and (2) Shape[] has no definition for RemoveAt and no extension for it.

(I'll have to add a Do While or another control to this as I cannot retrieve a shape from the empty array I hope to create once I'm able to get only a unique shape.)

Anyway, how do I get a shape without duplication?

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

(1) the RemoveAt request is unreachable and (2) Shape[] has no definition for RemoveAt and no extension for it.

These are simple syntax & logic errors.

The first one points out that if you return in both branches of the if statement, there's no way to get to code after the end of the if. return means you're done with this function and you pass control back to the calling code right away (with the exception of finally type blocks, that is)

eg...

var headsOrTails = FlipCoin();

if(headsOrTails == "heads") {
    // Half of the time, we'll stop here and report my victory.
    return "I win.";
} else {
    // The other half of the time, we'll stop here and report your victory.
    return "You win.";
}

// There's no way to reach this line of code.
// If we chose "heads" we exit inside the first branch.
// Otherwise we exit in the second branch.
// So either way, we've stopped executing the function before we get here.
return "the coin explodes?";

If you want to decide on a value to return, then do something else before you return it, you should save it to a variable like so:

string returnValue;
if(headsOrTails == "heads") {
    returnValue = "I win";
} else {
    returnValue = "You win";
}

DoSomethingElse();
return returnValue;

(2) points out that the array type doesn't have a RemoveAt method defined as one of the built-in things it can do. You can check its documentation to confirm. You might be thinking of the type List, which does have a RemoveAt method?

Basic C# syntax questions like this are things you can answer for yourself with a search through the language reference or our general programming sister site StackOverflow. Questions that are just about syntax with no game-specific requirements can be closed as off-topic here on GameDev.StackExchange, so please keep this in mind in future.


In any case, to select an item from a deck and then exclude that selection from future picks (until the deck is reshuffled), I'd do it like this:

public class ShuffledDeck<T> {

    // Local copy of the list of items.
    T[] items;
    // Count how many remain in the deck, not yet dealt.
    int remaining;

    // Create a new shuffled deck from an array of items.
    public ShuffledDeck(T[] items) {
        // Clone the array since we'll be re-ordering our copy.
        this.items = items.Clone();
        remaining = items.Length;
    }

    // Deal a random item from the ones not yet drawn.
    public T Draw() {

        // If we're out of items, draw nothing.
        // (Or, we can reshuffle our discards here instead)
        if(remaining == 0);
            return default(T);

        // Choose a random item from the bottom indices remaining.
        int chosenIndex = Random.Range(0, remaining);
        T chosen = items[chosenIndex];

        // Reduce the number of items we have left to draw from.
        remaining--;

        // Swap the item we chose with the last item not yet dealt.
        // (Might be itself, in which case this is a No-Op)
        items[chosenIndex] = items[remaining];
        items[remaining] = chosen;

        // Return the item we chose.
        return chosen;
    }

    // Reshuffle our discards so we can deal from a fresh deck.
    public void Reshuffle() {
        remaining = items.Length;
    }
}

This works like a Knuth or Fisher-Yates shuffle, partitioning our items array into two parts: the low indices are the ones we haven't yet dealt, and the high indices are the ones we've already dealt. By only selecting from the low indices, and swapping whatever we choose into the high indices, we maintain this partition after each draw.

\$\endgroup\$
1
  • \$\begingroup\$ I'm embarrassed about not heeding the return statements. Thanks especially for the example about saving to a variable before returning. And those shuffling methods are interesting. I read about them just a few days ago but don't recall ever using one. I upvoted you again but it won't show until my rep climbs to (or above?) 15. \$\endgroup\$
    – WPWPWP
    Sep 7, 2018 at 13:59

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .