﻿ Villager Move-out Probability - Animal Crossing: New Horizons Datamine

# Villager Move-out Probability

Thanks to @_Ninji for their scripts and tools which saved me a tremendous amount of time and energy getting everything set up, and for their datamine of villager move-outs.

This page is not a guide on how to move a villager out, but rather an examination of edge cases where the game’s logic breaks down.

If you notice any inaccuracies, please contact me on Discord (asteriation#6884) (preferred), or via email at acnh@isomorphicbox.com.

Please send any gameplay related questions to the ACNH Info Server.

## Background

each villager’s chance is based on friendship and calculated by floor((300 - a) / 10) - r

a = average friendship they have with all island residents
r = amount of residents with >200 friendship

## Implementation

The following is more or less what the game code does to pick a villager for the move-out prompt. (all integers are 32-bit here)

``````int num_players = ...;
int num_villagers = ...;
// bitmap of whether each villager can moveout (handling birthdays, etc.)
int can_moveout_bitmap = ...;
// friendship points for each villager:player pair
int friendship[10][8] = ...;

unsigned total_weight = 0;
signed moveout_weight[10] = { 0 };
for (int i = 0; i < num_villagers; ++i) {
if (can_moveout_bitmap & (1 << i)) {
int total_friendship = 0;
int num_lv6_friends = 0;
for (int j = 0; j < num_players; ++j) {
total_friendship += friendship[i][j];
if (GetFriendshipLevel(friendship[i][j]) > 5) {
++num_lv6_friends;
}
}
int avg_friendship = (int) ((1.0f / num_players) * total_friendship);
moveout_weight[i] = (int) ((300 - avg_friendship) * 0.1f) - num_lv6_friends;
total_weight += (unsigned) moveout_weight[i];
}
}

// random int in [0, total_weight), but 0 if total_weight == 0
unsigned random = (RandomU32() * total_weight) >> 0x20;
for (int i = 0; i < num_villagers; ++i) {
if ((random < (unsigned) moveout_weight[i]) &&
(0 < (signed) moveout_weight[i])) {
return i; // villager in slot i to ask to move out
}
random -= (unsigned) moveout_weight[i];
}
return -1; // no one will ask to move out
``````

## Villager Order

The game save contains an array of size 10 for villager data. This is populated in order as you get more and more villagers (starting with your first Sisterly villager in the first slot, first Jock villager in the second slot, and so on) until you reach 10 villagers. If a villager is instead replacing a moving out villager, the new villager takes the slot of the moving out villager (if your initial Jock villager got replaced, your new villager goes in the second slot).

This order is the order that the game goes in in the above code (and much other code in general); this will be relevant in a bit.

## Stopping Villager Move-out Prompts

It is possible to make any particular villager never ask to move-out, by ensuring `moveout_weight[i] <= 0` for the villager. This can be accomplished by having enough players on your island (at least 4 are required; they do not need to place the initial tent), with sufficiently high friendship:

Number of Players at 200+ Friendship Required Average Friendship
4 251.00 or higher
5 241.00 or higher
6 231.00 or higher
7 221.00 or higher
8 211.00 or higher

Satisfying this for every villager aside from the latest moved-in villager, and the villager who last asked to move out who you told to stay (if applicable) will result in there being no villager move-outs prompts whatsoever.

## Integer Underflow Bug

There are notable integer underflow bugs involving `moveout_weight` values being treated as an unsigned value, even though it may actually be negative if you have 5+ players at 200+ friendship:

Number of Players at 200+ Friendship Required Average Friendship for Integer Underflow
5 251.00 or higher
6 241.00 or higher
7 231.00 or higher
8 221.00 or higher

The `0 < moveout_weight[i]` check treats the value as a signed integer, which means that the bug does not affect the method described in the previous section, but it can have observable consequences on other villagers’ move-out probability.

There are two places where `moveout_weight[i] < 0` causes unexpected behavior:

``````total_weight += (unsigned) moveout_weight[i]
``````
• Negative values here make certain random rolls impossible, causing certain villagers’ move-out probabilities to decrease or even drop to zero
• If the sum of `moveout_weight[i]` is negative, `total_weight` (which is unsigned) will underflow, and the game will start rolling out of values just under 2^32 (4294967296); this will cause move-out probabilities for all villagers to drop to near (but not) zero
``````random -= (unsigned) moveout_weight[i];
``````
• Negative values here will shift the range of possibly random values upwards, which can make subsequent villager move-out probabilities less likely or impossible

### Examples

The following examples use a set of 8 villagers for simplicity; the villager who most recently moved in, and the villager who last asked to move out (and then got told to stay) are omitted (these are villagers 9 and 10).

The examples will also be assuming a total of 5 players on the island, all with the same number of friendship points for all villagers.

#### Example 1. Villager Can’t Move Out

• Villagers at 250 friendship (weight 0): Puck, Truffles
• Villagers at 255 friendship (weight -1): Gabi, Rhonda, Wendy, Lobo, Timbra
• Villagers at 200 friendship (weight 5): Ione

If the villager order is:

• Puck (0)
• Truffles (0)
• Gabi (-1)
• Rhonda (-1)
• Wendy (-1)
• Lobo (-1)
• Timbra (-1)
• Ione (5)

then since `total_weight = 0` in this case, the random roll will always return 0, and by the time to Ione, it would have been shifted upwards to 5. Since Ione has weight 5, this fails the check, and no one will ask to move out.

#### Example 2. Fixed Move-out Villager

• Villagers at 250 friendship (weight 0): Barold
• Villagers at 255 friendship (weight -1): Deena, Paolo, Diva
• Villagers at 235 friendship (weight 1): Ione, Marshal, Raymond, Sasha

Since `total_weight = 1` in this case, the random roll will always return 0, even though there are four villagers with `moveout_weight[i] > 0`.

If the villager order is:

• Barold (0)
• Ione (1)
• Marshal (1)
• Raymond (1)
• Sasha (1)
• Deena (-1)
• Paolo (-1)
• Diva (-1)

then the game will always make Ione ask to move out. However, if the order changes a bit, for example,

• Barold (0)
• Deena (-1)
• Ione (1)
• Marshal (1)
• Raymond (1)
• Sasha (1)
• Paolo (-1)
• Diva (-1)

then after Deena, the random roll has changed to 1, and Marshal will be the one to ask move out instead. Likewise, for

• Barold (0)
• Deena (-1)
• Paolo (-1)
• Diva (-1)
• Ione (1)
• Marshal (1)
• Raymond (1)
• Sasha (1)

the villager that always asks to move out will be Sasha instead.

#### Example 3. Extremely Low Move-out Probability

• Villagers at 250 friendship (weight 0): Barold
• Villagers at 255 friendship (weight -1): Deena, Paolo, Diva, Canberra
• Villagers at 235 friendship (weight 1): Ione, Raymond, Sasha

In this case, the sum of the `moveout_weight[i]` values is `-1`. Since `total_weight` is unsigned, this is treated as a value of 4294967295, and a number is picked in the range `[0, 4294967295)`. Due to bias (modulo bias, except we don't use modulos, but the same idea), 0 has a probability of `2/4294967296` or around 0.0000000466%, while all other values have a probability of `1/4294967296` or around 0.0000000233%.

In the case of the following villager order:

• Barold (0)
• Ione (1)
• Raymond (1)
• Sasha (1)
• Deena (-1)
• Paolo (-1)
• Diva (-1)
• Canberra (-1)

this results in:

Villager Move-out Chance
Ione 0.0000000466%
Raymond 0.0000000233%
Sasha 0.0000000233%
None 99.9999999069%

As in the previous example, changing the villager order may change the move-out chances here as well:

• Barold (0)
• Deena (-1)
• Paolo (-1)
• Ione (1)
• Raymond (1)
• Sasha (1)
• Diva (-1)
• Canberra (-1)

results in:

Villager Move-out Chance
Ione 0.0000000233%
Raymond 0.0000000000%
Sasha 0.0000000466%
None 99.9999999302%

and:

• Barold (0)
• Deena (-1)
• Paolo (-1)
• Diva (-1)
• Canberra (-1)
• Ione (1)
• Raymond (1)
• Sasha (1)

results in:

Villager Move-out Chance
Ione 0.0000000233%
Raymond 0.0000000233%
Sasha 0.0000000233%
None 99.9999999302%