Skip to content

Instantly share code, notes, and snippets.

@fbparis
Last active July 25, 2021 17:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fbparis/3d2511bdcb5ca8d4805add61a6352ecf to your computer and use it in GitHub Desktop.
Save fbparis/3d2511bdcb5ca8d4805add61a6352ecf to your computer and use it in GitHub Desktop.
Animal Crossing New Horizons happy villagers
"""
Use the formula found on https://nookipedia.com/wiki/Compatibility (slightly modified) to compute Animal Crossing New Horizons village compositions optimizing villagers happiness.
Compatibility score between 2 villagers is a grade from 0 (worst) to 4 (best) and village score is the sum of every combinations of 2 villagers score in the village.
You can pass a required list of villagers (using their english ids or french names) to find the ideal villagers to recrute next.
See --help for options. -m3 -r2 are usualy good parameters.
Note that the program never stops so you will have to hit CTRL-C when you're done!
If you pass 10 or more required villager, the program will output the best possible village(s) with these villagers.
"""
__author__ = 'fbparis@gmail'
__license__ = 'iv'
import argparse
import pickle
from collections import namedtuple, defaultdict
from itertools import combinations
from random import sample, choice, choices
fieldnames = 'id name french_name birthday species gender personality zodiac_sign zodiac_element'.split()
Villager = namedtuple('Villager', fieldnames)
def get_personality_score(personality_1, personality_2):
if set([personality_1, personality_2]) in (set(['lazy']), set(['peppy']), set(['jock']), set(['cranky']), set(['smug']), set(['uchi']), set(['normal', 'snooty']), set(['normal', 'smug']), set(['lazy', 'uchi']), set(['peppy', 'jock']), set(['snooty', 'cranky'])): return 'A'
if set([personality_1, personality_2]) in (set(['normal', 'jock']), set(['normal', 'uchi']), set(['lazy', 'peppy']), set(['lazy', 'cranky']), set(['peppy', 'snooty']), set(['jock', 'smug']), set(['snooty', 'smug']), set(['cranky', 'uchi'])): return 'B'
if set([personality_1, personality_2]) in (set(['normal']), set(['snooty']), set(['normal', 'cranky']), set(['lazy', 'snooty']), set(['lazy', 'smug']), set(['peppy', 'smug']), set(['peppy', 'uchi']), set(['jock', 'cranky']), set(['jock', 'uchi'])): return 'C'
return 'D'
def get_zodiac_score(zodiac_element_1, zodiac_element_2):
if zodiac_element_1 == zodiac_element_2: return 'A'
if set([zodiac_element_1, zodiac_element_2]) in (set(['fire', 'water']), set(['earth', 'air'])): return 'D'
return 'B'
def get_species_score(species_1, species_2):
if species_1 == species_2: return 'B'
if set([species_1, species_2]) in (set(['bear', 'cub']), set(['bull', 'cow']), set(['cat', 'tiger']), set(['dog', 'wolf']), set(['goat', 'sheep']), set(['kangaroo', 'koala'])): return 'A'
if set([species_1, species_2]) in (set(['deer', 'horse']), set(['hamster', 'squirrel']), set(['hamster', 'mouse']), set(['mouse', 'squirrel'])): return 'B'
if set([species_1, species_2]) in (set(['cat', 'mouse']), set(['cat', 'hamster']), set(['dog', 'gorilla']), set(['dog', 'monkey']), set(['sheep', 'wolf'])): return 'D'
return 'C'
def get_compatibility_score(villager_1, villager_2, alt=False):
score = get_classic_compatibility_score(villager_1, villager_2)
if alt:
score /= 2
# if villager_1.personality != villager_2.personality: score += 2
if villager_1.gender != villager_2.gender: score += 2/3
if villager_1.species != villager_2.species: score += 2/3
if villager_1.personality != villager_2.personality: score += 2/3
return score
def get_classic_compatibility_score(villager_1, villager_2):
results = sorted([get_personality_score(villager_1.personality, villager_2.personality), get_species_score(villager_1.species, villager_2.species), get_zodiac_score(villager_1.zodiac_element, villager_2.zodiac_element)])
if results.count('A') == 3: return 4
if results.count('A') == 2 or results in (['A', 'B', 'C'], ['A', 'B', 'B']): return 3
if results.count('D') == 3: return 0
if results.count('D') == 2: return 1
return 2
def load_villagers():
with open('villagers.pickle', 'rb') as f:
villagers = pickle.load(f)
return villagers
def get_village_score(village, balanced=False, alt=False):
score = 0
for (villager_1, villager_2) in combinations(village, 2):
score += get_compatibility_score(villager_1, villager_2, alt=alt)
if balanced:
score += get_balance_score(village)
return score
def get_balance_score(village):
"""
"""
gender_count = 1
species_count = 9
personality_count = 7
village_gender = defaultdict(int)
village_species = defaultdict(int)
village_personality = defaultdict(int)
for villager in village:
village_gender[villager.gender] += 1
village_species[villager.species] += 1
village_personality[villager.personality] += 1
village_gender = [x for x in village_gender.values()]
village_species = [x for x in village_species.values()]
village_personality = [x for x in village_personality.values()]
gender_score = 9 - ((len(village_gender) - 1) / gender_count) * (9 - max(village_gender) + min(village_gender))
species_score = 9 - ((len(village_species) - 1) / species_count) * (9 - max(village_species) + min(village_species))
personality_score = 9 - ((len(village_personality) - 1) / personality_count) * (10 - max(village_personality) + min(village_personality))
village_norm = sum([x**2 for x in (gender_score, species_score, personality_score)])**0.5
return .999 * (1 - village_norm / 243**.5)
def best_villages_with(villagers, balanced=False, alt=False, french=False):
"""
"""
best = []
best_score = 0
for village in combinations(villagers, 10):
score = get_village_score(village, balanced=balanced, alt=alt)
if score >= best_score:
if score > best_score:
best_score = score
best = []
best.append(village)
print('\nBEST SCORE FOUND: %.2f' % best_score)
for village in best:
if french:
print('\n %s' % ', '.join(sorted([v.french_name for v in village])))
else:
print('\n %s' % ', '.join(sorted([v.name for v in village])))
props = zip(*[(v.gender, v.species, v.personality) for v in village])
props = zip(['genders', 'species', 'personalities'],[[(x, prop.count(x)) for x in set(prop)] for prop in props])
for prop, values in props:
print(' %s: %s' % (prop, ', '.join(['%d %s' % (c, v) for v, c in values])))
if alt:
other_score = 'classic score: %d' % get_village_score(village, alt=False)
else:
other_score = 'alternative score: %.2f' % get_village_score(village, alt=True)
print(' balance score: %.3f; %s' % (get_balance_score(village), other_score))
def random_walk(village, villagers, required, scores, steps, alt=False):
"""
we use weighted random, less popular villager most likely chosen for replacement, then most popular substitute most likely chosen
"""
for step in range(steps):
best_move = []
# first select a villager to replace
weights = []
for villager in village:
weights.append(sum([v for k, v in scores.items() if villager.id not in k]))
score_without_villager, villager = choices([(weights[i], villager) for i, villager in enumerate(village)], weights=[weight**2 for weight in weights], k=1)[0]
# now select a subsitute
weights = []
for subsitute in villagers:
if subsitute in village:
continue
score = score_without_villager
_scores = {}
for other in village + required:
if other == villager:
continue
_score = get_compatibility_score(subsitute, other, alt=alt)
_scores[(subsitute.id, other.id)] = _score
score += _score
best_move.append((subsitute, _scores))
weights.append(score**2)
# make the replacement
(subsitute, _scores) = choices(best_move, weights=weights, k=1)[0]
village = [v for v in village if v != villager] + [subsitute]
scores = dict([(k, v) for k, v in scores.items() if villager.id not in k] + [(k, v) for k, v in _scores.items()])
return sum([score for score in scores.values()]), village, scores
def improve_village(village, villagers, required, random_steps=None, alt=False):
"""
Replace a villager with another to maximize village score at each step
When no more replacement can be done, return the new village
As fast as we can, never compute the same thing twice (but still slow)
"""
scores = {}
best_score = 0
for villager_1, villager_2 in combinations(village + required, 2):
score = get_compatibility_score(villager_1, villager_2, alt=alt)
scores[(villager_1.id, villager_2.id)] = score
best_score += score
if random_steps is None:
steps = max(0, 9 - len(required))
else:
steps = random_steps
if steps > 0:
# randomly switch some villagers then try to improve the resulting village
best_score, village, scores = random_walk(village, villagers, required, scores, steps, alt)
while 1:
best_move = []
best_score += 1 # new solution must be better to avoid infinite loops
for villager in village:
score_without_villager = sum([v for k, v in scores.items() if villager.id not in k])
for substitute in villagers:
if substitute in village:
continue
score = score_without_villager
_scores = {}
for other in village + required:
if other == villager:
continue
_score = get_compatibility_score(substitute, other, alt=alt)
_scores[(substitute.id, other.id)] = _score
score += _score
if score >= best_score:
if score > best_score:
best_score = score
best_move = []
best_move.append((villager, substitute, _scores))
if not best_move:
break
# If several replacements have the same score, randomly choose one
(villager, substitute, _scores) = choice(best_move)
village = [v for v in village if v != villager] + [substitute]
scores = dict([(k, v) for k, v in scores.items() if villager.id not in k] + [(k, v) for k, v in _scores.items()])
return best_score - 1, village
def generate_villages(villagers, base=None, random_steps=None, keep_best=False, max_repetitions=None, balanced=False, alt=False, french=False):
"""
find villages that maximize the compatibility between its villagers
can be seeded with a list of required villagers
"""
required = set()
if base is not None:
base = set([x.lower() for x in base])
for villager in villagers.values():
found = False
for name in base:
if (french == True and name == villager.french_name.lower()) or (french == False and name == villager.id.lower()):
found = True
required.add(villager)
break
if found:
base.remove(name)
if not base:
break
if len(required) >= 10:
best_villages_with(required, balanced, alt, french)
else:
villagers = [villager for villager in villagers.values() if villager not in required]
required = list(required)
best_score = -1000
n = 10 - len(required)
results = set()
best_village = sample(villagers, n)
repeat = 0
history = set()
if required:
if french:
required_output = ', '.join(['%s' % v.french_name for v in required])
else:
required_output = ', '.join(['%s' % v.name for v in required])
if len(required) < 10:
required_output += ' + '
else:
required_output = ''
while 1:
score, village = improve_village(best_village, villagers, required, random_steps, alt=alt)
balance_score = get_balance_score(village + required)
if alt:
other_score = 'classic score: %d' % get_village_score(village + required)
else:
other_score = 'alternative score: %.2f' % get_village_score(village + required, balanced=balanced, alt=True)
if balanced:
score += balance_score
if french:
last = tuple(sorted(['%s' % v.french_name for v in village]))
else:
last = tuple(sorted(['%s' % v.name for v in village]))
if last in history:
repeat += 1
else:
if max_repetitions is not None:
history.add(last)
if score >= best_score:
best_village = village
if last not in results:
print()
if score > best_score:
best_score = score
results = set()
repeat = 0
print('NEW BEST SCORE WITH %.2f POINTS\n' % score)
results.add(last)
props = zip(*[(v.gender, v.species, v.personality) for v in village + required])
props = zip(['genders', 'species', 'personalities'],[[(x, prop.count(x)) for x in set(prop)] for prop in props])
print(' %s%s' % (required_output, ', '.join(last)))
for prop, values in props:
print(' %s: %s' % (prop, ', '.join(['%d %s' % (c, v) for v, c in values])))
print(' balance score: %.3f; %s' % (balance_score, other_score))
if (max_repetitions is not None and repeat > max_repetitions) or (random_steps is not None and random_steps <= 0):
# if too much repetitions of the same position, or if random walk not used then regenerate a fresh new random village
best_village = sample(villagers, n)
repeat = 0
elif not keep_best:
# restart from the last generated village
best_village = village
if __name__ == '__main__':
best_village_found = "BEST FOUND SO FAR WITH 147 POINTS: Bianca, Felicity, Kid Cat, Pinky, Poncho, Rudy, Stinky, Tabby, Tutu, Tybalt (genders: 5 female, 5 male; species: 2 bear, 5 cat, 1 cub, 2 tiger; personalities: 5 jock, 5 peppy)"
parser = argparse.ArgumentParser(description=__doc__, epilog=best_village_found)
parser.add_argument('--villagers', '-v', nargs='*', help='list of space sperated names for required villagers')
parser.add_argument('--random-steps', '-r', type=int, default=None, help='number of random walk steps to escape local maxima ; automatic by default, 0 to skip random walk and try improving a random village at each step')
parser.add_argument('--max-repetitions', '-m', type=int, default=None, help='regenerate a random village to improve when getting too much known results ; not used by default')
parser.add_argument('--keep-best', '-k', action='store_true', help='only try to improve the best solution found')
parser.add_argument('--balanced', '-b', action='store_true', help='extend scoring to boost well balanced villages (gender, species and personality)')
parser.add_argument('--alt', '-a', action='store_true', help='alternative ranking mixing villagers compatibility and village balance')
parser.add_argument('--french', '-f', action='store_true', help='use villagers french name')
args = parser.parse_args()
try:
generate_villages(load_villagers(), base=args.villagers, random_steps=args.random_steps, keep_best=args.keep_best, max_repetitions=args.max_repetitions, balanced=args.balanced, alt=args.alt, french=args.french)
except KeyboardInterrupt:
pass
"""
Create a CSV of Animal Crossing New Horizons from:
- https://github.com/jefflomacy/villagerdb/tree/master/data/villagers for name, id, birthday, species, gender, personality
- https://nookipedia.com/wiki/List_of_villager_names_in_other_languages for french name
- utility functions to convert birthday to zodiac sign and element
1) check and/or update VILLAGERS_PATH_MASK
2) save source html of https://nookipedia.com/wiki/List_of_villager_names_in_other_languages in List_of_villager_names_in_other_languages.html
3) call create_csv()
4) Manualy check/fix villagers.csv (name and french_name can be wrong)
5) call create_pickle() when done
6) you can now use animal-crossing-village.py
"""
__author__ = 'fbparis.com'
__license__ = 'iv'
import csv
import json
import pickle
from bs4 import BeautifulSoup
from collections import namedtuple
from glob import glob
VILLAGERS_PATH_MASK = 'villagerdb-master/data/villagers/*.json'
fieldnames = 'id name french_name birthday species gender personality zodiac_sign zodiac_element'.split()
Villager = namedtuple('Villager', fieldnames)
def get_zodiac_sign_from_birthday(birthday):
month, day = birthday.split('-')
md = (int(month), int(day))
if md >= (1, 20) and md <= (2, 18): return 'aquarius'
if md >= (2, 19) and md <= (3, 20): return 'pisces'
if md >= (3, 21) and md <= (4, 19): return 'aries'
if md >= (4, 20) and md <= (5, 20): return 'taurus'
if md >= (5, 21) and md <= (6, 20): return 'gemini'
if md >= (6, 21) and md <= (7, 22): return 'cancer'
if md >= (7, 23) and md <= (8, 22): return 'leo'
if md >= (8, 23) and md <= (9, 22): return 'virgo'
if md >= (9, 23) and md <= (10, 22): return 'libra'
if md >= (10, 23) and md <= (11, 21): return 'scorpio'
if md >= (11, 22) and md <= (12, 21): return 'sagittarius'
return 'capricorn'
def get_zodiac_element_from_zodiac_sign(zodiac_sign):
if zodiac_sign in ('aquarius', 'gemini', 'libra'): return 'air'
if zodiac_sign in ('pisces', 'cancer', 'scorpio'): return 'water'
if zodiac_sign in ('aries', 'leo', 'sagittarius'): return 'fire'
return 'earth'
def create_csv():
to_french = dict()
with open('List_of_villager_names_in_other_languages.html') as f:
html = f.read()
soup = BeautifulSoup(html)
for tr in soup.select('tbody tr'):
td = tr.find_all('td')
if not td:
continue
name = td[0].a.text
if name in to_french:
print('problem with %s' % name)
to_french[name] = td[3].text
with open('villagers.csv', 'w', newline='') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
for json_file in glob(VILLAGERS_PATH_MASK):
with open(json_file) as f:
data = json.load(f)
if 'nh' not in data['games']:
continue
french_name = to_french[data['name']]
zodiac_sign = get_zodiac_sign_from_birthday(data['birthday'])
zodiac_element = get_zodiac_element_from_zodiac_sign(zodiac_sign)
villager = Villager(data['id'], data['name'], french_name, data['birthday'], data['species'], data['gender'], data['games']['nh']['personality'], zodiac_sign, zodiac_element)
writer.writerow(villager._asdict())
def create_pickle():
villagers = {}
with open('villagers.csv', newline='') as csvfile:
reader = csv.DictReader(csvfile, fieldnames=fieldnames)
for row in reader:
villager = Villager(**row)
villagers[villager.id] = villager
check_villagers(villagers)
with open('villagers.pickle', 'wb') as f:
pickle.dump(villagers, f)
def check_villagers(villagers):
names = set()
french_names = set()
for villager in villagers.values():
names.add(villager.name)
french_names.add(villager.french_name)
print('%d villagers / %d names / %d french names' % (len(villagers), len(names), len(french_names)))
if __name__ == '__main__':
#create_csv()
create_pickle()
admiral Admiral Maréchal 1-27 bird male cranky aquarius air
agent-s Agent S Ninjette 7-2 squirrel female peppy cancer water
agnes Agnes Pansy 4-21 pig female uchi taurus earth
al Al Gustave 10-18 gorilla male lazy libra air
alfonso Alfonso Alphonse 6-9 alligator male lazy gemini air
alice Alice Alice 8-19 koala female normal leo fire
alli Alli Allie 11-8 alligator female snooty scorpio water
amelia Amelia Aurélie 11-19 eagle female snooty scorpio water
anabelle Anabelle Anabelle 2-16 anteater female peppy aquarius air
anchovy Anchovy Miguel 3-4 bird male lazy pisces water
angus Angus Angus 4-30 bull male cranky taurus earth
anicotti Anicotti Annie 2-24 mouse female peppy pisces water
ankha Ankha Neferti 9-22 cat female snooty virgo earth
annalisa Annalisa Roberta 2-6 anteater female normal aquarius air
annalise Annalise Âne-lise 12-2 horse female snooty sagittarius fire
antonio Antonio Antonio 10-20 anteater male jock libra air
apollo Apollo Apollon 7-4 eagle male cranky cancer water
apple Apple Esther 9-24 hamster female peppy libra air
astrid Astrid Rhona 9-8 kangaroo female snooty virgo earth
audie Audie Monica 8-31 wolf female peppy virgo earth
aurora Aurora Aurore 1-27 penguin female normal aquarius air
ava Ava Eva 4-28 chicken female normal taurus earth
avery Avery Faust 2-22 eagle male cranky pisces water
axel Axel Axel 3-23 elephant male jock aries fire
baabara Baabara Bêêtty 3-28 sheep female snooty aries fire
bam Bam Nacer 11-7 deer male jock scorpio water
bangle Bangle Bengale 8-27 tiger female peppy virgo earth
barold Barold Manu 3-2 cub male lazy pisces water
bea Bea Béa 10-15 dog female normal libra air
beardo Beardo Eustache 9-27 bear male smug libra air
beau Beau Stefaon 4-5 deer male lazy aries fire
becky Becky Sonya 12-9 chicken female snooty sagittarius fire
bella Bella Belle 12-28 mouse female peppy capricorn earth
benedict Benedict Dimitri 10-10 chicken male lazy libra air
benjamin Benjamin Bernardo 8-3 dog male lazy leo fire
bertha Bertha Bertha 4-25 hippo female normal taurus earth
bettina Bettina Sabrina 6-12 mouse female normal gemini air
bianca Bianca Noémie 12-13 tiger female peppy sagittarius fire
biff Biff Biff 3-29 hippo male jock aries fire
big-top Big Top Miles 10-3 elephant male lazy libra air
bill Bill Choco 2-1 duck male jock aquarius air
billy Billy Seguin 3-25 goat male jock aries fire
biskit Biskit Crocket 5-13 dog male lazy taurus earth
bitty Bitty Potama 10-6 hippo female snooty libra air
blaire Blaire Cachou 7-3 squirrel female snooty cancer water
blanche Blanche Sophie 12-21 ostrich female snooty sagittarius fire
bluebear Bluebear Myrtille 6-24 cub female peppy cancer water
bob Bob Robert 1-1 cat male lazy capricorn earth
bonbon Bonbon Sylvette 3-3 rabbit female peppy pisces water
bones Bones Nonos 8-4 dog male lazy leo fire
boomer Boomer Ethan 2-7 penguin male lazy aquarius air
boone Boone Babouin 9-12 gorilla male jock virgo earth
boots Boots Croko 8-7 alligator male jock leo fire
boris Boris Boris 11-6 pig male cranky scorpio water
boyd Boyd Primo 10-1 gorilla male cranky libra air
bree Bree Quenotte 7-7 mouse female snooty cancer water
broccolo Broccolo Steven 6-30 mouse male lazy cancer water
broffina Broffina Jo 10-24 chicken female snooty scorpio water
bruce Bruce Boubou 5-26 deer male cranky gemini air
bubbles Bubbles Hippy 9-18 hippo female peppy virgo earth
buck Buck Daniel 4-4 horse male jock aries fire
bud Bud Léonard 8-8 lion male jock leo fire
bunnie Bunnie Clara 5-9 rabbit female peppy taurus earth
butch Butch Avril 11-1 dog male cranky scorpio water
buzz Buzz Phébus 12-7 eagle male cranky sagittarius fire
cally Cally Célia 9-4 squirrel female normal virgo earth
camofrog Camofrog Milos 6-5 frog male cranky gemini air
canberra Canberra Kolala 5-14 koala female uchi taurus earth
candi Candi Sucrette 4-13 mouse female peppy aries fire
carmen2 Carmen Zoé 1-6 rabbit female peppy capricorn earth
caroline Caroline Isabelle 7-15 squirrel female normal cancer water
carrie Carrie Kanga 12-5 kangaroo female normal sagittarius fire
cashmere Cashmere Cashmir 4-2 sheep female snooty aries fire
celia Celia Garance 3-25 eagle female normal aries fire
cesar Cesar César 9-6 gorilla male cranky virgo earth
chadder Chadder Mozzar 12-15 mouse male smug sagittarius fire
charlise Charlise Zabou 4-17 bear female uchi aries fire
cheri Cheri Rosalie 3-17 cub female peppy pisces water
cherry Cherry Anna 5-11 dog female uchi taurus earth
chester Chester Placide 8-6 cub male lazy leo fire
chevre Chevre Biquette 3-6 goat female normal pisces water
chief Chief Chef 12-19 wolf male cranky sagittarius fire
chops Chops Aaron 10-13 pig male smug libra air
chow Chow Chulin 7-22 bear male cranky cancer water
chrissy Chrissy Kristine 8-28 rabbit female peppy virgo earth
claude Claude Claude 12-3 rabbit male lazy sagittarius fire
claudia Claudia Vanina 11-22 tiger female snooty sagittarius fire
clay Clay Guido 10-19 hamster male lazy libra air
cleo Cleo Cléa 2-9 horse female snooty aquarius air
clyde Clyde Dorian 5-1 horse male lazy taurus earth
coach Coach Arnold 4-29 bull male jock taurus earth
cobb Cobb Porken 10-7 pig male jock libra air
coco Coco Coco 3-1 rabbit female normal pisces water
cole Cole Épicure 8-10 rabbit male lazy leo fire
colton Colton Tony 5-22 horse male smug gemini air
cookie Cookie Cookie 6-18 dog female peppy gemini air
cousteau Cousteau Figaro 12-17 frog male jock sagittarius fire
cranston Cranston Gabin 9-23 ostrich male lazy libra air
croque Croque Carlos 7-18 frog male cranky cancer water
cube Cube Cube 1-29 penguin male lazy aquarius air
curlos Curlos Tonton 5-8 sheep male smug taurus earth
curly Curly Tirbou 7-26 pig male jock leo fire
curt Curt Curt 7-1 bear male cranky cancer water
cyd Cyd Punk 6-9 elephant male cranky gemini air
cyrano Cyrano Cyrano 3-9 anteater male cranky pisces water
daisy Daisy Naomie 11-16 dog female normal scorpio water
deena Deena Mina 6-27 duck female normal cancer water
deirdre Deirdre Bichoune 5-4 deer female uchi taurus earth
del Del Hector 5-27 alligator male cranky gemini air
deli Deli Magogo 5-24 monkey male lazy gemini air
derwin Derwin Prof 5-25 duck male lazy gemini air
diana Diana Didi 1-4 deer female snooty capricorn earth
diva Diva Violette 10-2 frog female uchi libra air
dizzy Dizzy Pachy 7-14 elephant male lazy cancer water
dobie Dobie Loupiot 2-17 wolf male cranky aquarius air
doc Doc Doc 3-16 rabbit male lazy pisces water
dom Dom Bouloche 3-18 sheep male jock pisces water
dora Dora Dora 2-18 mouse female normal aquarius air
dotty Dotty Dorothée 3-14 rabbit female peppy pisces water
drago Drago Drago 2-12 alligator male lazy aquarius air
drake Drake Colvert 6-25 duck male lazy cancer water
drift Drift Gordon 10-9 frog male jock libra air
ed Ed Édouard 9-16 horse male smug virgo earth
egbert Egbert Herbert 10-14 chicken male lazy libra air
elise Elise Élise 3-21 monkey female snooty aries fire
ellie Ellie Ella 5-12 elephant female normal taurus earth
elmer Elmer Martin 10-5 horse male lazy libra air
eloise Eloise Éloïse 12-8 elephant female snooty sagittarius fire
elvis Elvis Elvis 7-23 lion male cranky leo fire
erik Erik Abraham 7-27 deer male lazy leo fire
eugene Eugene Jamy 10-26 koala male smug scorpio water
eunice Eunice Bérénice 4-3 sheep female normal aries fire
fang Fang Pierrot 12-18 wolf male cranky sagittarius fire
fauna Fauna Bibi 3-26 deer female normal aries fire
felicity Felicity Maud 3-30 cat female peppy aries fire
filbert Filbert Filibert 6-3 squirrel male lazy gemini air
flip Flip Rudy 11-21 monkey male jock scorpio water
flo Flo Nora 9-2 penguin female uchi virgo earth
flora Flora Justine 2-9 ostrich female peppy aquarius air
flurry Flurry Emma 1-30 hamster female normal aquarius air
francine Francine Nadine 1-22 rabbit female snooty aquarius air
frank Frank Greggae 7-30 eagle male cranky leo fire
freckles Freckles Caro 2-19 duck female peppy pisces water
freya Freya Luppa 12-14 wolf female snooty sagittarius fire
friga Friga Friga 10-16 penguin female snooty libra air
frita Frita Clarabêl 7-16 sheep female uchi cancer water
frobert Frobert Verbert 2-8 frog male jock aquarius air
fuchsia Fuchsia Rosanne 9-19 deer female uchi virgo earth
gabi Gabi Gaby 12-16 rabbit female peppy sagittarius fire
gala Gala Camille 3-5 pig female normal pisces water
gaston Gaston Gaston 10-28 rabbit male cranky scorpio water
gayle Gayle Odile 5-17 alligator female normal taurus earth
genji Genji Kali 1-21 rabbit male jock aquarius air
gigi Gigi Gloria 8-11 frog female snooty leo fire
gladys Gladys Gladys 1-15 ostrich female normal capricorn earth
gloria Gloria Déborah 8-12 duck female snooty leo fire
goldie Goldie Mirza 12-27 dog female normal capricorn earth
gonzo Gonzo Gonzo 10-13 koala male cranky libra air
goose Goose Pouli 10-4 chicken male jock libra air
graham Graham Graham 6-20 hamster male smug gemini air
greta Greta Greta 9-5 mouse female snooty virgo earth
grizzly Grizzly Grizzly 7-31 bear male cranky leo fire
groucho Groucho Ronchon 10-23 bear male cranky scorpio water
gruff Gruff Grognon 8-29 goat male cranky virgo earth
gwen Gwen Gwen 1-23 penguin female snooty aquarius air
hamlet Hamlet Jojo 5-30 hamster male jock gemini air
hamphrey Hamphrey Charles 2-25 hamster male cranky pisces water
hans Hans Loran 12-5 gorilla male smug sagittarius fire
harry Harry Bob 1-7 hippo male cranky capricorn earth
hazel2 Hazel Pamela 8-30 squirrel female uchi virgo earth
henry Henry Henri 9-21 frog male smug virgo earth
hippeux Hippeux Paulito 10-15 hippo male smug libra air
hopkins Hopkins Grignote 3-11 rabbit male lazy pisces water
hopper Hopper Victor 4-6 penguin male cranky aries fire
hornsby Hornsby Cornio 3-20 rhino male lazy pisces water
huck Huck Bajoue 7-9 frog male smug cancer water
hugh Hugh Bonno 12-30 pig male lazy capricorn earth
iggly Iggly Urbain 11-2 penguin male jock scorpio water
ike Ike Isaac 5-16 bear male cranky taurus earth
jacob Jacob Jacob 8-24 bird male lazy virgo earth
jacques Jacques Jacky 6-22 bird male smug cancer water
jambette Jambette Gambette 10-27 frog female normal scorpio water
jay Jay Gérard 7-17 bird male jock cancer water
jeremiah Jeremiah Jérémie 7-8 frog male lazy cancer water
jitters Jitters Gilbert 2-2 bird male jock aquarius air
joey Joey Joseph 1-3 duck male lazy capricorn earth
judy Judy Laura 3-10 cub female snooty pisces water
julia Julia Julie 7-31 ostrich female snooty leo fire
julian Julian Lico 3-15 horse male smug pisces water
june June Agnès 5-21 cub female normal gemini air
kabuki Kabuki Kabuki 11-29 cat male cranky sagittarius fire
katt Katt Kat 4-27 cat female uchi taurus earth
keaton Keaton Enzo 6-1 eagle male smug gemini air
ken Ken Ken 12-23 chicken male smug capricorn earth
ketchup Ketchup Ketchup 7-27 duck female peppy leo fire
kevin Kevin Jean-Bon 4-26 pig male jock taurus earth
kid-cat Kid Cat Câlin 8-1 cat male jock leo fire
kidd Kidd Moktar 6-28 goat male smug cancer water
kiki Kiki Kiki 10-8 cat female normal libra air
kitt Kitt Poquette 10-11 kangaroo female normal libra air
kitty Kitty Kitty 2-15 cat female snooty aquarius air
klaus Klaus Klaus 3-31 bear male smug aries fire
knox Knox Wolfram 11-23 chicken male cranky sagittarius fire
kody Kody Bill 9-28 cub male jock libra air
kyle Kyle Gary 12-6 wolf male smug sagittarius fire
leonardo Leonardo Dolph 5-15 tiger male jock taurus earth
leopold Leopold Leandro 8-14 lion male smug leo fire
lily Lily Raina 2-4 frog female normal aquarius air
limberg Limberg Gruyère 10-17 mouse male cranky libra air
lionel Lionel Léopold 7-29 lion male smug leo fire
lobo Lobo Lobo 11-5 wolf male cranky scorpio water
lolly Lolly Linette 3-27 cat female normal aries fire
lopez Lopez Jon 8-20 deer male smug leo fire
louie Louie Louis 3-26 gorilla male jock aries fire
lucha Lucha Condor 12-12 bird male smug sagittarius fire
lucky Lucky Ramsès 11-4 dog male lazy scorpio water
lucy Lucy Lucie 6-2 pig female normal gemini air
lyman Lyman Kalyptus 10-12 koala male jock libra air
mac Mac Brutus 11-11 dog male jock scorpio water
maddie Maddie Olympe 1-11 dog female peppy capricorn earth
maelle Maelle Maëlle 4-8 duck female snooty aries fire
maggie Maggie Marjorie 9-3 pig female normal virgo earth
mallary Mallary Mallory 11-17 duck female snooty scorpio water
maple Maple Léa 6-15 cub female normal gemini air
marcel Marcel Ismaël 12-31 dog male lazy capricorn earth
marcie Marcie Selma 5-31 kangaroo female normal gemini air
margie Margie Maguy 1-28 elephant female normal aquarius air
marina Marina Marina 6-26 octopus female normal cancer water
marshal Marshal Mathéo 9-29 squirrel male smug libra air
mathilda Mathilda Mathilde 11-12 kangaroo female snooty scorpio water
megan Megan Candy 3-13 bear female normal pisces water
melba Melba Melba 4-12 koala female normal aries fire
merengue Merengue Patty 3-19 rhino female normal pisces water
merry Merry Suzy 6-29 cat female peppy cancer water
midge Midge Michèle 3-12 bird female normal pisces water
mint Mint Amande 5-2 squirrel female snooty taurus earth
mira Mira Grisette 7-6 rabbit female uchi cancer water
miranda Miranda Maëllis 4-23 duck female snooty taurus earth
mitzi Mitzi Mistigri 9-25 cat female normal libra air
moe Moe Momo 1-12 cat male lazy capricorn earth
molly Molly Molly 3-7 duck female normal pisces water
monique Monique Monique 9-30 cat female snooty libra air
monty Monty Lourant 12-7 monkey male cranky sagittarius fire
moose Moose Joachim 9-13 mouse male jock virgo earth
mott Mott Aimé 7-10 lion male jock cancer water
muffy Muffy Charlène 2-14 sheep female uchi aquarius air
murphy Murphy Eddie 12-29 cub male cranky capricorn earth
nan Nan Nana 8-24 goat female normal virgo earth
nana Nana Mireille 8-23 monkey female normal virgo earth
naomi Naomi Maiko 2-28 cow female snooty pisces water
nate Nate Nathan 8-16 bear male lazy leo fire
nibbles Nibbles Lola 7-19 squirrel female peppy cancer water
norma Norma Norma 9-20 cow female normal virgo earth
octavian Octavian Octave 9-20 octopus male cranky virgo earth
ohare O'Hare Civet 7-24 rabbit male smug leo fire
olaf Olaf Blair 5-19 anteater male smug taurus earth
olive Olive Grisa 7-12 cub female normal cancer water
olivia Olivia Olivia 2-3 cat female snooty aquarius air
opal Opal Opaline 1-20 elephant female snooty aquarius air
ozzie Ozzie Koko 5-7 koala male lazy taurus earth
pancetti Pancetti Lydie 11-14 pig female snooty scorpio water
pango Pango Mathilda 11-9 anteater female peppy scorpio water
paolo Paolo Paolo 5-5 elephant male lazy taurus earth
papi Papi Bourrico 1-10 horse male lazy capricorn earth
pashmina Pashmina Chavrina 12-26 goat female uchi capricorn earth
pate Pate Terrine 2-23 duck female peppy pisces water
patty Patty Margaux 5-10 cow female peppy taurus earth
paula Paula Wendy 3-22 bear female uchi aries fire
peaches Peaches Prune 11-28 horse female normal sagittarius fire
peanut Peanut Rachida 6-8 squirrel female peppy gemini air
pecan Pecan Pécan 9-10 squirrel female snooty virgo earth
peck Peck Pec 7-25 bird male jock leo fire
peewee Peewee Gogo 9-11 gorilla male cranky virgo earth
peggy Peggy Rose 5-23 pig female peppy gemini air
pekoe Pekoe Pauline 5-18 cub female normal taurus earth
penelope Penelope Missy 2-5 mouse female peppy aquarius air
phil Phil Phil 11-27 ostrich male smug sagittarius fire
phoebe Phoebe Faustine 4-22 ostrich female uchi taurus earth
pierce Pierce Napoléon 1-8 eagle male jock capricorn earth
pietro Pietro Pietro 4-19 sheep male smug aries fire
pinky Pinky Rosine 9-9 bear female peppy virgo earth
piper Piper Neige 4-18 bird female peppy aries fire
pippy Pippy Nadia 6-14 rabbit female peppy gemini air
plucky Plucky Poulette 10-12 chicken female uchi libra air
pompom Pompom Pompon 2-11 duck female peppy aquarius air
poncho Poncho Théo 1-2 cub male jock capricorn earth
poppy Poppy Irène 8-5 squirrel female normal leo fire
portia Portia Dalma 10-25 dog female snooty scorpio water
prince Prince Prince 7-21 frog male lazy cancer water
puck Puck Hervé 2-21 penguin male lazy pisces water
puddles Puddles Rénata 1-13 frog female peppy capricorn earth
pudge Pudge Gradub 6-11 cub male lazy gemini air
punchy Punchy Cédric 4-11 cat male lazy aries fire
purrl Purrl Perle 5-29 cat female snooty gemini air
queenie Queenie Reine 11-13 ostrich female snooty scorpio water
quillson Quillson Narcisse 12-22 duck male smug capricorn earth
raddle Raddle Fabien 6-6 frog male lazy gemini air
rasher Rasher Salami 4-7 pig male cranky aries fire
raymond Raymond Raymond 10-1 cat male smug libra air
renee Renée Rina 5-28 rhino female uchi gemini air
reneigh Reneigh Jennifer 6-4 horse female uchi gemini air
rex Rex Léo 7-24 lion male lazy leo fire
rhonda Rhonda Renée 1-24 rhino female normal aquarius air
ribbot Ribbot Crabot 2-13 frog male jock aquarius air
ricky Ricky Rocky 9-14 squirrel male cranky virgo earth
rizzo Rizzo Sourizzi 1-17 mouse male cranky capricorn earth
roald Roald Reynald 1-5 penguin male jock capricorn earth
robin Robin Robie 12-4 bird female snooty sagittarius fire
rocco Rocco Bebel 8-18 hippo male cranky leo fire
rocket Rocket Gertrude 4-14 gorilla female uchi aries fire
rod Rod Marcel 8-14 mouse male jock leo fire
rodeo Rodeo Flèche 10-29 bull male lazy scorpio water
rodney Rodney Chico 11-10 hamster male smug scorpio water
rolf Rolf Ralf 8-22 tiger male cranky leo fire
rooney Rooney Mike 12-1 kangaroo male cranky sagittarius fire
rory Rory Hercule 8-7 lion male jock leo fire
roscoe Roscoe Rosco 6-16 horse male cranky gemini air
rosie Rosie Rosie 2-27 cat female peppy pisces water
rowan Rowan Marito 8-26 tiger male jock virgo earth
ruby Ruby Rubis 12-25 rabbit female peppy capricorn earth
rudy Rudy Rougepif 12-20 cat male jock sagittarius fire
sally2 Sally Damia 6-19 squirrel female normal gemini air
samson Samson Samson 7-5 mouse male jock cancer water
sandy Sandy Ottie 10-21 ostrich female normal libra air
savannah Savannah Savana 1-25 horse female normal aquarius air
scoot Scoot Scooter 6-13 duck male jock gemini air
shari Shari Luna 4-10 monkey female uchi aries fire
sheldon Sheldon Roy 2-26 squirrel male jock pisces water
shep Shep Mehdi 11-24 dog male smug sagittarius fire
sherb Sherb Capri 1-18 goat male lazy capricorn earth
simon Simon Simon 1-19 monkey male lazy capricorn earth
skye Skye Marilou 3-24 wolf female normal aries fire
sly Sly Chuck 11-15 alligator male jock scorpio water
snake Snake Jeannot 11-3 rabbit male jock scorpio water
snooty Snooty Tarina 10-24 anteater female snooty scorpio water
soleil Soleil Stella 8-9 hamster female snooty leo fire
sparro Sparro Darius 11-20 bird male jock scorpio water
spike Spike Rhino 6-17 rhino male cranky gemini air
spork Spork Justin 9-3 pig male lazy virgo earth
sprinkle Sprinkle Laurie 2-20 penguin female peppy pisces water
sprocket Sprocket Laflèche 12-1 ostrich male jock sagittarius fire
static Static Électro 7-9 squirrel male cranky cancer water
stella Stella Bigoudi 4-9 sheep female normal aries fire
sterling Sterling Manfred 12-11 eagle male jock sagittarius fire
stinky Stinky Tupux 8-17 cat male jock leo fire
stitches Stitches Miro 2-10 cub male lazy aquarius air
stu Stu Beubeu 4-20 bull male lazy taurus earth
sydney Sydney Koaline 6-21 koala female normal cancer water
sylvana Sylvana Mounia 10-22 squirrel female normal libra air
sylvia Sylvia Madsi 5-3 kangaroo female uchi taurus earth
t-bone T-Bone Steakos 5-20 bull male cranky taurus earth
tabby Tabby Tigri 8-13 cat female peppy leo fire
tad Tad Rénato 8-3 frog male jock leo fire
tammi Tammi Lili 4-2 monkey female peppy aries fire
tammy Tammy Vanessa 6-23 cub female uchi cancer water
tangy Tangy Marine 6-17 cat female peppy gemini air
tank Tank Ben 5-6 rhino male jock taurus earth
tasha Tasha Nadeige 11-30 squirrel female snooty sagittarius fire
teddy Teddy Teddy 9-26 bear male jock libra air
tex Tex Émilien 10-6 penguin male smug libra air
tia Tia Fanny 11-18 elephant female normal scorpio water
tiffany Tiffany Tiphaine 1-9 rabbit female snooty capricorn earth
timbra Timbra Sélène 10-21 sheep female snooty libra air
tipper Tipper Valé 8-25 cow female snooty virgo earth
tom Tom Tom 12-10 cat male cranky sagittarius fire
truffles Truffles Trufa 7-28 pig female peppy leo fire
tucker Tucker Barry 9-7 elephant male lazy virgo earth
tutu Tutu Tutu 9-15 bear female peppy virgo earth
twiggy Twiggy Titi 7-13 bird female peppy cancer water
tybalt Tybalt Jeff 8-19 tiger male jock leo fire
ursala Ursala Oursula 1-16 bear female uchi capricorn earth
velma Velma Véra 1-14 goat female snooty capricorn earth
vesta Vesta Hélaine 4-16 sheep female normal aries fire
vic Vic Toto 12-29 bull male cranky capricorn earth
victoria Victoria Victoria 7-11 horse female peppy cancer water
violet Violet Gaëlle 9-1 gorilla female snooty virgo earth
vivian Vivian Viviane 1-26 wolf female snooty aquarius air
vladimir Vladimir Vladimir 8-2 cub male cranky leo fire
wade Wade Miglou 10-30 penguin male lazy scorpio water
walker Walker George 6-10 dog male lazy gemini air
walt Walt Walt 4-24 kangaroo male cranky taurus earth
wart-jr Wart Jr. Crakos 8-21 frog male cranky leo fire
weber Weber Bébert 6-30 duck male lazy cancer water
wendy Wendy Karen 8-15 sheep female peppy leo fire
whitney Whitney Blanche 9-17 wolf female snooty virgo earth
willow Willow Maï 11-26 sheep female snooty sagittarius fire
winnie Winnie Anne 1-31 horse female peppy aquarius air
wolfgang Wolfgang Wolfgang 11-25 wolf male cranky sagittarius fire
yuka Yuka Calypso 7-20 koala female snooty cancer water
zell Zell Régis 6-7 deer male smug gemini air
zucker Zucker Marvin 3-8 octopus male lazy pisces water
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment