Welsh Castles Relay Results — Help

WCR Emblem
Help FAQ Feedback About

Help

This site provides searchable access to historic Welsh Castles Relay results from 1983 onward, covering every year the race has been held (excluding 2001 when Foot & Mouth cancelled the event, and 2020–2021 when no race took place due to COVID-19). These results came from mostly scans of paper results held here WCR Historic Results

Navigation

The site is organised into the following top-level pages:

  • Stage Results — view individual stage results by year and stage
  • Overall Results — final team standings by year, with route map for 2018+
  • Runner Search — search athletes by name, club, year range, and more
  • Team Appearances — browse clubs and their participation history
  • Statistics — aggregate stats, records, and special category winners
  • Help — this page, plus FAQ, Feedback, and About (tabbed layout)

Stage Results

Select a year and stage to view the full results for that stage. Results are ordered by finishing position. Use the Previous / Next links to navigate between stages. Each entry shows the runner's name, club, category (Open / Ladies / Vets / Business House), time, and age category. Runners marked NRE (No Runner Entered) indicate the team did not field a runner on that stage and received a penalty time.

Below the distance and elevation header, the stage page shows the current male and female stage record (holder name, formatted time, and year set). Records carry forward year-to-year within the same stage distance. When a stage distance changes, the winner sets a new baseline record — flagged with "(New stage record)".

Where GPX data is available, the stage location name is a clickable link that opens an interactive route map and elevation profile. Use the size toggle to switch between small and large views.

Media Links

When a stage has associated media content (e.g. videos, photos, external coverage), a red play icon appears in the top-right corner of the stage results section. Each click opens the next linked URL in a new tab, cycling through all available media links for that stage.

Overall Results

Select a year to see the final team standings. Teams are ranked by total time across all stages. Only teams with a complete set of stage results (one runner on every stage) appear in the standings. The table shows each team's club, category, total time, and stage count.

For years 2018 onwards, the Overall Team Results heading is a clickable link that opens an interactive route map showing all 20 stage routes overlaid on a single map. A summary box shows the winning team's total time, total distance, total climb, and average pace per mile. Each stage is drawn in a distinct colour (cycling through black, red, purple, dark blue, dark green). This works for 2018–2026 where the current stage routes have been in use. Earlier years are excluded because stage routes differed before 2018.

Runner Search

Search for athletes by name, club, year range, stage, category, sex, or age category.

Name search supports boolean operators:

  • John Smith — both terms must match (AND)
  • John OR Smith — either term matches
  • John NOT Smith or John -Smith — exclude a term
  • "Les Croupiers" John — quoted phrase AND term
  • Dav% — raw SQL LIKE pattern (use % as wildcard)

Click Advanced filters to narrow by club, year range, stage, race category, sex, or age category. All filters are combinable and appear in the URL for bookmarking.

Click the Stage column header to toggle the sort order between (year, stage) and (stage, year), useful when comparing runners across the same stage in different years.

Team Appearances

Browse all clubs that have participated in the Welsh Castles Relay, with the years they competed and total entries. Clubs are grouped by normalised name and category. The page is organised into three tabs:

  • Appearances Count — ranks club-category combinations with 10 or more appearances. For each club-category row it shows the number of appearances (years fielded a team), Wins (overall category championships in red bold), Top 10 finishes (absolute overall top-10 placings in green bold), and Entry Counts (per-year team counts by category). Club names link to a finish position graph showing the team's overall finishing position in each year they competed, plotted as a line chart with position on the y-axis (1 at top) and year on the x-axis, plus a summary table of finish positions.
  • Finish Position History — shows a per-year breakdown of overall finishing positions for each club across their years of participation. Toggle the view to switch between all clubs and a single club.

Statistics

View aggregate statistics across all years of data. The page is organised into tabs:

  • Totals — unique teams and runners, top 10 by appearances, stage wins, and female finishes, top 5 club winners by year.
  • Stage Records — course records for each stage across all years. Select a stage number to see the fastest male and female times on record for that stage. Records carry forward year-to-year when the stage distance remains the same or shorter. Use the Year filter to focus on a specific year. The table shows the record time, the athlete who set it, and the stage distance. Click the stage number heading to open a Google My Maps map showing all 20 stage routes.
  • Fastest Wins — the top 50 stage-winning performances ranked by pace (time per mile). This view highlights the fastest individual stage victories across all years and categories. Each entry shows the runner's name, club, year, stage, winning time, distance, pace, and total elevation gain/loss for that stage.
  • Outright Female Winners — women who finished first overall on a stage.
  • Women Top 5% — extraordinary performances by women finishing in the top 5% of a stage.
  • Kings of the Mountains — the Open team with the lowest cumulative time on mountain stages each year (1990+). Click a team name to see a modal with the stage-by-stage breakdown of runners and times on the mountain stages that year.
  • Queens of the Mountains — the Ladies team with the lowest cumulative time on mountain stages each year (1994+). Click a team name for the per-stage breakdown.
  • Monarchs of the Mountains — the Veterans team with the lowest cumulative time on mountain stages each year (2002+). Click a team name for the per-stage breakdown.
  • Jeff Wood Winners — the first V50 runner on a designated non-mountain stage each year. Official list from the Les Croupiers Roll of Honour (1999–2024). From 2025 onward, computed automatically from runner DOB and age on race day.

About the data

Results from 1983 to 2006 were extracted from scanned JPG images/PDF using OCR (optical character recognition). Results from 2007 onward were parsed from PDF files. The most recent years (2022+) are loaded from Google Sheets exports. Cross-checking every result against the original source materials is an ongoing process — it takes time, but that is always the goal.

Data sources:

  • 1983–2006: A variety of formats in PDFs/JPGs dictated or extracted with Tesseract OCR or Google Gemini and my smartphone
  • 2007–2019: PDF stage result files
  • 2022–present: Google Sheets exports from the race organisers

Feedback & Contact

Found an error in the data or have a suggestion? Use the Feedback page to report issues or submit ideas for improvements. All feedback is welcome and helps maintain data accuracy.

FAQ

Frequently asked questions about the database, data sources, and how results are recorded.

About

Learn more about the Welsh Castles Relay, this results database, and the project's history.

Frequently Asked Questions

Hopefully to intercept various comments, questions, yes-buts, issues and limitations.

  1. This website is very much a first attempt but all the data (in some form) 1983-2026 is in (except when the event was not held).
  2. Person names are a nightmare to work with — nicknames, people who are David, Dave, Davey Boy, Dai, D, "Tiger" etc depending on the day of the week — yes they are in here.
  3. Real duplicate names shared by a number of different people — if your surname is Davies, Williams, Jones, Smith etc and your first name is not Zaphod or Moonunit then you will have issues with other people who share your name — as you probably already know. Currently as we do not have UKA Numbers for more than a third of the names in the database if you have a common name and you are listed wrongly in some way, use the feedback form and explain exactly what needs to change.
  4. There are honest spelling mistakes and basic guesswork at what an illegible name is. There are something like 80 occasions where the same name appears to have run twice (or more) for the same team in the same year - that's what the paper results show.
  5. There are 101 ways of spelling a club name and people explore every one — I have tried to standardise.
  6. Will it work on a phone? It's usable but better on a large screen.
  7. Stage records - I thought a lot about this, what seems fair is that a record carries forward as long as the stage distance does not increase, at this point it must be replaced by the new best at the longer distance. A related issue to the variable nature of the stage lengths is that when running pace is displayed, this is based on the stage lengths that have not been measured by Jones Counter, at best they are Garmin GPS measurements.
  8. "Why a database when we have scans?" I agree, let's abolish Google and look through scans of things.
  9. "The website is very sparse, no dancing penguins, no enormous logos taking up 70% of the page" — intentionally so.
  10. There is no WCR history page, social media etc — best to do that last really or you end up changing it over and over.
  11. "Age categories seem to be all over the place" — Yes they are. The official veteran thresholds have changed twice: 1983–1999 men and women both at 40; 2000–2008 men 40, women 35; 2009–2012 both at 35; 2013–present men 40, women 35 (sources: 1998/2000 programmes, History of WCR). The database has a config mapping these rules but is not yet enforcing them retroactively because (a) we don't know if team managers always followed the official thresholds when declaring runners, (b) the scanned results record age category inconsistently, and (c) there are basic omissions and errors in declaration, recording, input and scanning. A future pass could apply the rules with a DOB check to flag likely category errors.
  12. Irregular capitalisation — tried to deal with that with rules, problem being that names can be very arbitrary with capitalisation.
  13. Stray weird characters, yes these are OCR artifacts, basically I will have to go through every stage of every year manually.
  14. If a value is in green then clicking it will do something.
  15. "Not great for people with Red/Green colourblindness" — I think that ship sailed in the 1970s or whenever the club vest was designed.
  16. "Who are you and why have you done this?" - I am a Les Croupiers member involved in the WCR organisation, I appear in this database as a participant 8 times but was not running for Les Croupiers at the time. I have been involved every year it has been held since 2008 (except 2023) all the way from Caernarfon. I hope this clarifies the first point, the second point is because I wanted to view the historic results properly and do them justice.

Feedback/Queries/Feature Requests

Please use the form below to send us your feedback, queries or feature requests about the Welsh Castles Relay Historic Results Database. All submissions are read. This is meant for subjects not already covered by the Help and FAQ documents.

If you have a question regarding taking part at a future Castles Relay then you could email [email protected]

About This Database

Commit 356 · 2f8b79c · 2026-08-18 16:19:55 +0100

The Welsh Castles Relay Historic Results Database catalogues stage-by-stage results for every running of the Welsh Castles Relay from 1983 onward — every team, every runner, every stage.

For the uninitiated, the Welsh Castles Relay is a 20 stage running relay from Caernarfon Castle to close to Cardiff Castle. It is not now a continuous ''touch'' relay, rather 20 races over the first weekend of every June. It can quite justifiably be called the premier league of road relays.

In case anyone wants to know the technical details (you probably don't). This website runs on a Raspberry Pi 5 with 8GB RAM and a NVME drive (because I am not a total madman). It runs command line 64 bit Debian as the OS. UPDATE - I discovered the Pi was running very hot in the heatwave (80C) so I took the pragmatic decision to migrate to a Minisforum Mini PC which has a Ryzen 7900 CPU and 32GB RAM running Linux Mint - a bit sad but realistically a sensible option. The application is Python with Flask using Postgres as the database. I am using Cloudflare to look after the tricky bits. My internet connection is 300MBits/sec down and 48 MBits/sec up which is probably my limiting factor. I don't know at which point my ISP will decide that I have breached the TOS for traffic. Fortunately there is nothing heavyweight being served so we might get away with it. What did this all cost? Mainly my time, the Pi was already controlling my NAS, my network and my VPN and the Domain is $5 per year.

The underlying schema is shown below as a Mermaid entity–relationship diagram. Referential integrity, partial unique indexes and constraints enforce data quality; the application layer handles normalisation of club names, athlete merges and the carry-forward of stage records across distance eras.

erDiagram ATHLETE { int athlete_id PK text athlete_name text former_name varchar sex date dob text uka_no } ATHLETE_CHANGE_LOG { int athlete_id text athlete_name varchar sex text former_name date dob text uka_no varchar operation datetime changed_at } STAGE { int year PK int stage_number PK text stage_name float distance int male_record text male_record_holder int female_record text female_record_holder varchar mountain_stage boolean manually_corrected varchar jw int start_elevation int end_elevation int elevation_gain_m int elevation_loss_m int net_elevation_change_m text strava_activity_url float start_latitude float start_longitude float end_latitude float end_longitude date race_date int cutoff_time } STAGE_CUTOFFS { int year PK int stage_no PK int cutoff } RESULT { int result_id PK int year FK int stage_number FK int athlete_id FK text club int club_running_number int time int position text race_category text age_category text dnf_nre } RESULT_CHANGE_LOG { int result_id int year int stage_number int athlete_id text club int club_running_number int time int position text race_category text age_category varchar operation datetime changed_at } OVERALL_CATEGORY { int year PK int club_running_number PK text category } MEDIA { int year PK int stage_number PK text url1 text url2 text url3 text url4 text url5 } PAGEVIEW { int id PK datetime viewed_at text ip_address text label int duration_ms } IP_CACHE { text ip_address PK text country text hostname text city text isp text asn datetime cached_at boolean is_bot text ipv6_prefix } EDITABLE_PAGE { text slug PK text title text body datetime updated_at } FEEDBACK { int id PK datetime submitted_at text name text email text comment } TRAFFIC_ROLLUP { boolean id PK int total_pageviews datetime last_updated boolean archived_mode datetime last_full_cleanup } TRAFFIC_SEEN_IP { text ip_address PK text first_seen } STATS_UPDATED { boolean id PK datetime updated_at } PORTAL_CONFIG { boolean id PK date open_date date close_date boolean manual_override text portal_password_hash text portal_pin_hash boolean test_mode datetime updated_at } ENTRY { int entry_id PK text club int year text race_category text manager_name text manager_email text password_hash text status int club_running_number datetime created_at datetime updated_at } ENTRY_RUNNER { int entry_runner_id PK int entry_id FK text uka_no text first_name text last_name varchar sex date dob text medical_issues } YEAR_STAGE_RUNNER_LIST { int id PK int year int stage_number int club_running_number text club text race_category int athlete_id FK int entry_id FK datetime created_at datetime updated_at } APPLICATION { int application_id PK int year text club_name text email text race_category text club_affiliation text manager_name text manager_position text address1 text address2 text address3 text address4 text address5 text postcode text manager_mobile text backup_contact text backup_mobile datetime submitted_at text status int team_number text notes varchar reference_code datetime created_at datetime updated_at } DB_CONFIG { text key PK text value text description datetime updated_at } ATHLETE ||--o{ RESULT : "runs in" STAGE ||--o{ RESULT : "produces" STAGE ||--o{ MEDIA : "links to" STAGE ||--o{ STAGE_CUTOFFS : "cutoff for" ENTRY ||--o{ ENTRY_RUNNER : "contains" IP_CACHE ||--o{ PAGEVIEW : "matches ip_address" YEAR_STAGE_RUNNER_LIST ||--o| ATHLETE : "assigned to" YEAR_STAGE_RUNNER_LIST ||--o| ENTRY : "belongs to" ATHLETE ||--o{ ATHLETE_CHANGE_LOG : "audit trail" RESULT ||--o{ RESULT_CHANGE_LOG : "audit trail"