Inspiration
When roommates pick a place together, someone always gives something up, and nobody can see the trade-offs. Usually the loudest person, or whoever found the listing, wins.
We wanted a tool that treats everyone's wishes equally and shows its work. Instead of averaging everyone's happiness, it looks after the least happy person.
Here's why averages fail. Say Place A scores you 95, Sam 90 and Jordan 30. Place B scores 75, 70 and 70. Both average 72, so an average can't tell them apart. We pick B, because nobody is miserable.
What it does
Common Ground picks the apartment where the least happy roommate is happiest, and explains why.
- Say what you want, in your own words. For example: "Under $1,600 each, in-unit laundry, close to a gym, my own bathroom." Add where you go most days and how many people are in the group.
- See real listings on a map. Active rentals around your campus or office, each with real walk, bike, transit and drive times.
- Add your roommates by phone. They text our iMessage bot whatever they want, with no app or account. Every text fills in or changes their wishes ("actually I need parking").
- Get the fairest pick, with the math. The pick updates by itself. The app names who gives up the most and why ("Alex: 44-minute commute, −15.4 points").
- Everyone stays in the loop. When someone's text changes the pick, everyone else gets an "Update" text with a view-only map link that opens on any phone.
A real reply from the bot, after Sam texts "max $1,900, in-unit laundry is a must, and I'd love a coffee shop nearby":
- "Got it, Sam: rent: important; in-unit laundry: must-have; near a coffee shop: important; budget $1,900/mo. Fairest pick for Alex, Sam & Jordan: 1615 Greenfield Ave (Unit 9), $1,598 each. You: 79.2/100. Least happy: Alex (74.9), mostly because of a 44-minute commute and rent at $1,598 each."
Nothing is guessed. If no listing or map can answer a wish ("safe neighborhood"), the bot says so and leaves it out of the score.
How we built it
One rule shaped everything: the AI reads words; our code does the math. Gemini never produces a score, a ranking or a distance.
- Gemini (Google AI Studio) turns listings and people's texts into structured facts. Each fact must come with an exact quote.
- Our validation code keeps a fact only if its quote appears in the original text, and any number must appear inside that quote. Everything else is thrown out.
- Google Maps Platform (Routes, Places) gives real walk, bike, transit and drive times, and the nearest gym, coffee shop or Trader Joe's to each listing.
- RentCast API finds real active rentals around any place. Near UCLA, one request returned 500 within 2 miles.
- Scoring (Python) scores each person 0–100 per listing from their own weighted wishes, with a breakdown.
- Fair pick (Python) uses maximin: it picks the place where the lowest individual score is highest.
- Photon (iMessage) carries roommates' texts to the bot and the bot's replies and Update texts back.
- FastAPI, Leaflet + OpenStreetMap run the backend and the map page.
- A Cloudflare quick tunnel gives a view-only results link; only the results page answers through it.
Texts and listings flow left to right; Gemini's output only reaches the scores after our code finds every quote in the original words.
A few scoring rules, all in our own code:
- Words become weights: must 5, important 4.5, wanted 4, neutral 3, nice to have 2, don't care 0.
- Commute scores full at 10 minutes and zero at 40. "Near X" scores full at a 5-minute walk and zero at 25.
- Rent scores full at 75% of your budget and zero at 2× your budget.
- Unknown facts are left out of a score, never filled in, and the listing is marked "partial data".
Every Gemini, Google and RentCast answer is cached, so the demo works with no internet and repeat runs cost $0. The web page and the bot share one group file behind a file lock, so they never overwrite each other.
Challenges we ran into
- Keeping the AI honest. A model will happily invent "pets allowed" or "gas included". We made every fact carry an exact quote and check it in code, so an unbacked fact never reaches a score.
- The per-room trap. Some listings show one room's price next to the whole house's bedrooms ("7 bed, $1,080"). If the price per bedroom is under 40% of the area's typical, we flag it and never split it.
- Budgets below market. Near UCLA a typical 3-bedroom is about $2,000 per person, so a $1,000 budget made every listing score zero. We now let rent reach zero only at 2× the budget, so a tight budget still ranks places.
- The bot can't text first. Photon's shared iMessage line can only reply, so each roommate sends one text to start.
- Two writers, one group. The web page and the bot edit the same group at the same time. A file lock keeps every edit; in testing, 300 edits at once lost none.
- No double texts. When Photon fails, replies are retried but never sent twice. Update texts go out at most once per person every 3 minutes, always with the newest pick.
- A tiny API budget. RentCast's free plan allows 50 requests a month. One area is one request, and we stop at 45 so we're never charged.
Accomplishments that we're proud of
- It works on real phones. We tested the whole loop with our own iPhones: text the bot, watch the card fill, see the pick flip, get the Update text, open the map.
- Fairness you can see. The app names the least happy person and the exact reasons, so the group can argue about facts instead of feelings.
- "Near X" works for anything: a gym, Trader Joe's, a dog park, Chipotle, a baseball field. Google finds the nearest one to each listing and the real walk there.
- Stress-tested. These are the results:
| Test | Result |
| 730 requests at once | No errors |
| 6 programs making 300 edits at the same time. | None lost |
| A flood of 100 texts | No double replies; 2 Update texts instead of 48 |
| Photon failures | Replies retried, never sent twice |
| Hostile input (script tags, emoji, huge texts, junk numbers) | Handled |
| Every route tried through the public link | Only the results page answers |
| No internet | The demo still works from cache |
We also wrote 132 automated tests, none of which call the internet or send a text.
What we learned
- Use AI to read, not to judge. Gemini is great at turning messy texts into structure. Scoring and ranking belong in plain code people can check.
- "Unknown" is an honest answer. Leaving a missing fact out of a score was fairer than guessing it.
- The definition of fair changes the answer. Averages hide the person who loses; maximin puts them first.
- Real data is messy. Per-room prices, missing laundry info and below-market budgets all broke our first version.
What's next for Common Ground
- A hosted version with separate groups (a group link or code), so it runs without our laptop.
- Laundry, parking and utilities for every live listing. RentCast doesn't include them yet, so today they count as unknown unless someone pastes the listing.
- Group chats, so the whole house can talk to the bot together.
- Removing a wish by text. Today people can add or change wishes, but only "reset" clears them.
Today it runs on one laptop, works over iMessage only, and covers US listings in US dollars.
Built With
- cloudflare
- css
- fastapi
- gemini
- google-ai-studio
- google-maps
- google-places
- google-routes-api
- html
- imessage
- javascript
- leaflet.js
- openstreetmap
- photon
- python
- rentcast

Log in or sign up for Devpost to join the conversation.