adapted generation of statistics and json results of maintenance script to new layout

This commit is contained in:
Trilarion 2019-07-08 13:10:05 +02:00
parent 2915f52a19
commit 59b96e0878
5 changed files with 1240 additions and 1837 deletions

View File

@ -1,27 +1,27 @@
# Open Source Games # Open Source Games
**[Dynamic HTML table](https://trilarion.github.io/opensourcegames/)** of the entries / Development **[Blog](https://trilarion.blogspot.com/search/label/osgames)** / **[Statistics](games/statistics.md)** **[Dynamic HTML table](https://trilarion.github.io/opensourcegames/)** of the entries / Development **[Blog](https://trilarion.blogspot.com/search/label/osgames)** / **[Statistics](statistics.md#statistics)**
[comment]: # (start of autogenerated content, do not edit) [comment]: # (start of autogenerated content, do not edit)
- **[All](games/_all.md)** (517) - **[All](games/_all.md#All)** (517)
- **[Action](games/_action.md)** (69) - **[Action](games/_action.md#Action)** (69)
- **[Arcade](games/_arcade.md)** (10) - **[Arcade](games/_arcade.md#Arcade)** (10)
- **[Adventure](games/_adventure.md)** (14) - **[Adventure](games/_adventure.md#Adventure)** (14)
- **[Visual novel](games/_visual novel.md)** (4) - **[Visual novel](games/_visual novel.md#Visual novel)** (4)
- **[Sports](games/_sports.md)** (8) - **[Sports](games/_sports.md#Sports)** (8)
- **[Platform](games/_platform.md)** (2) - **[Platform](games/_platform.md#Platform)** (2)
- **[Puzzle](games/_puzzle.md)** (18) - **[Puzzle](games/_puzzle.md#Puzzle)** (18)
- **[Role playing](games/_role playing.md)** (117) - **[Role playing](games/_role playing.md#Role playing)** (117)
- **[Simulation](games/_simulation.md)** (33) - **[Simulation](games/_simulation.md#Simulation)** (33)
- **[Strategy](games/_strategy.md)** (168) - **[Strategy](games/_strategy.md#Strategy)** (168)
- **[Card game](games/_card game.md)** (1) - **[Card game](games/_card game.md#Card game)** (1)
- **[Board game](games/_board game.md)** (9) - **[Board game](games/_board game.md#Board game)** (9)
- **[Music](games/_music.md)** (3) - **[Music](games/_music.md#Music)** (3)
- **[Educational](games/_educational.md)** (3) - **[Educational](games/_educational.md#Educational)** (3)
- **[Tool](games/_tool.md)** (15) - **[Tool](games/_tool.md#Tool)** (15)
- **[Game engine](games/_game engine.md)** (7) - **[Game engine](games/_game engine.md#Game engine)** (7)
- **[Framework](games/_framework.md)** (59) - **[Framework](games/_framework.md#Framework)** (59)
- **[Library](games/_library.md)** (22) - **[Library](games/_library.md#Library)** (22)
[comment]: # (end of autogenerated content) [comment]: # (end of autogenerated content)
A list of open source games sorted by genre. The projects are at least in beta stage with a code basis that builds A list of open source games sorted by genre. The projects are at least in beta stage with a code basis that builds
@ -30,7 +30,7 @@ modification and sharing by others. For each entry, relevant information is coll
download possibilities and build instructions. download possibilities and build instructions.
Similar collections include [Open Source Clones](https://github.com/opengaming/osgameclones) of popular games; Similar collections include [Open Source Clones](https://github.com/opengaming/osgameclones) of popular games;
Popular games, add-ons, maps, etc. [hosted on GitHub](https://github.com/leereilly/games); [List of open-source video games](https://en.wikipedia.org/wiki/List_of_open-source_video_games) on Wikipedia. Popular games, add-ons, maps, etc. [hosted on GitHub](https://github.com/leereilly/games); [List of open-source video games](https://en.wikipedia.org/wiki/List_of_open-source_video_games) on Wikipedia or the [LibreGameWiki](https://libregamewiki.org/Main_Page).
## Contribute ## Contribute

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,8 @@
<body> <body>
<div class="container"> <div class="container">
<h2>List of Open Source Games</h2> <h2>List of Open Source Games</h2>
<p>List of open source games in beta or mature state under a license that allows sharing and modification. The list is managed on <a href="https://github.com/Trilarion/opensourcegames">Github</a> (see also the <a href="https://trilarion.blogspot.com/search/label/osgames">Blog</a>). For feedback (additions, corrections, ..) use the <a href="https://github.com/Trilarion/opensourcegames/issues">Issue tracker</a>. Some <a href="https://github.com/Trilarion/opensourcegames/blob/master/games/statistics.md#statistics">statistics</a> about the games.</p> <p>List of open source games in beta or mature state under a license that allows sharing and modification. The list is managed on <a href="https://github.com/Trilarion/opensourcegames">Github</a> (see also the <a href="https://trilarion.blogspot.com/search/label/osgames">Blog</a>). For feedback (additions, corrections, ..) use the <a href="https://github.com/Trilarion/opensourcegames/issues">Issue tracker</a>. Some <a href="https://github.com/Trilarion/opensourcegames/blob/master/statistics.md#statistics">statistics</a> about the games.</p>
<p> Other collections: <a href="https://osgameclones.com/">Open Source Game Clones</a>, <a href="https://github.com/leereilly/games/blob/master/README.md">Games on Github</a>, <a href="https://en.wikipedia.org/wiki/List_of_open-source_video_games">Open source games (Wikipedia)</a></p> <p> Other collections: <a href="https://osgameclones.com/">Open Source Game Clones</a>, <a href="https://github.com/leereilly/games/blob/master/README.md">Games on Github</a>, <a href="https://en.wikipedia.org/wiki/List_of_open-source_video_games">Open source games (Wikipedia)</a>, <a href="https://libregamewiki.org/Main_Page">LibreGameWiki</a></p>
<p><h4>Features</h4></p> <p><h4>Features</h4></p>
<p> <p>
<ul> <ul>

View File

@ -1,7 +1,7 @@
[comment]: # (autogenerated content, do not edit) [comment]: # (autogenerated content, do not edit)
# Statistics # Statistics
analyzed 517 entries on 2019-07-04 14:01:29 analyzed 517 entries on 2019-07-08 13:09:04
## State ## State
@ -11,7 +11,7 @@ analyzed 517 entries on 2019-07-04 14:01:29
##### Inactive State ##### Inactive State
Blobby Volley 2 (2017), Eat The Whistle (2017), PokerTH (2017), Scrolling Game Development Kit 2 (2017), Tressette (2017), YSoccer (2017), Zone of Control (2017), 3d.city (2016), Atlantis (2016), Atomic Tanks (2016), Atrinik (2016), Birth of the Empires (2016), DNT (2016), Dark Destiny (2016), Dungeon Monkey Eternal (2016), FreeRCT (2016), GLtron (2016), Infiniminer (2016), JaNaG - Java Name Generator (2016), Mechanized Assault & eXploration Reloaded (2016), Open Yahtzee (2016), Polis (2016), TORCS, The Open Racing Car Simulator (2016), Tremulous (2016), ransack (2016), Aleph One: Marathon (2015), Battleround (2015), Blobwars: Metal Blob Solid (2015), Boulder Dash (2015), D-Fend Reloaded (2015), Dash Engine (2015), DeSmuME (2015), Delta Engine (2015), Egoboo (2015), Free heroes2 engine (2015), Liquid War (2015), M.E.W.L. (2015), Roguish (2015), Scrabble3D (2015), Tenes Empanadas Graciela (2015), The Endless Dungeons (2015), The Epic of Heroes (2015), TwinEngine (2015), Witch Blast (2015), pyORPG (2015), Argentum Online (2014), Battles of Antargis (2014), BlockOut II (2014), Cart Life (2014), Deity (2014), Dune Dynasty (2014), EternalWinterWars (2014), FAR Colony (2014), Fujo (2014), Grobots (2014), Hale (2014), Heroes of Wing Commander (2014), Kingdoms (2014), Lips of Suna (2014), Open RPG Maker (2014), Open Tibia (2014), Radakan (2014), SDL Game Engine 2D (2014), SDL Sopwith (2014), Scorched 3D (2014), Sintel The Game (2014), Summoning Wars (2014), Supremacy (2014), Turious (2014), VDrift (2014), X-Moto (2014), Xenowar (2014), rpge (2014), sandbox Game Maker (2014), Bos Wars (2013), C-evo (2013), Candy Box 2 (2013), Decker (2013), DemiGod (2013), Dungeon Mapper (2013), HoDoKu (2013), Holyspirit (2013), MechCommander 2 Omnitech (2013), Mpango (2013), Murder In The Public Domain (2013), OpenRPG (2013), Phantasy Star Rebirth (2013), Room for Change (2013), SoftPixel Engine (2013), Source of Tales (2013), SpaceZero (2013), The Bub's Brothers (2013), Trinity Reign (2013), Zero Ballistics (2013), Biniax (2012), BlackNova Traders (2012), Blitzkrieg (2012), Cataclysm (2012), Celestron (2012), CommandoJS (2012), Conquests (2012), Dark City (2012), Frozen Bubble (2012), Goblin Camp (2012), Hexwar (2012), Mega Mario (2012), OpenArena (2012), PARPG (2012), TROPHY (2012), Thousand Parsec (2012), Tux Football (2012), UFO2000 (2012), Wizards Magic (2012), OpenBlox (2011), Aeron (2011), Battlefield Java (2011), Brain Workshop (2011), Danger from the Deep (2011), Dawn (2011), Fish Fillets - Next Generation (2011), GalaxyMage Redux (2011), KQ Lives (2011), Kobold's Quest 2 (2011), OpenMOO2 (2011), PyKaraoke (2011), Skrupel - Tribute Compilation (2011), Smash Battle (2011), Song of Albion (2011), Star Control II: The Ur-Quan Masters (2011), Tux of Math Command (2011), UlDunAd (2011), World of Phaos (2011), X-Force: Fight For Destiny (2011), AI Wars (2010), Bombermaaan (2010), Crimson Fields (2010), Dragon History - Dračí Historie (2010), Heroes of Wesnoth (2010), Labyrinth of Worlds (2010), Linwarrior 3D (2010), Mercenary Commander (2010), OpenHoMM (2010), Slay (2010), SpaceTrader for Java (2010), UltraStar (2010), XPilot (2010), XSera (2010), nXtank (2010), Alien Assault Traders (2009), Annchienta (2009), Ardentryst (2009), Battle Tanks (2009), Crown and Cutlass (2009), Dark Oberon (2009), Duel Commander (2009), Fall of Imyrin (2009), Fallen Spire (2009), FreeTrain (2009), Glest (2009), Goblin Hack (2009), MicroWar 2.0 (2009), Netrek (2009), ORIENT (2009), OpenPop (2009), SDL Asylum (2009), Secret Mario Chronicles (2009), Space Trader for Windows (2009), World of Heroes (2009), Yo Frankie! (2009), A Planet's Revenge (2008), Attal: Lords of doom (2008), Avanor (2008), Freestars (2008), Mars, Land of No Mercy (2008), Meritous (2008), Open Game Engine (2008), S.C.O.U.R.G.E. (2008), Sengoku: Warring States of Japan (2008), Space Opera (2008), Armies (2007), Brutal Chess (2007), Eos, Dawn of Light: A Space Opera (2007), Pang Zero (2007), SharpKonquest (2007), Silvertree (2007), Slash'EM (2007), Underworld Adventures (2007), World Builder (2007), kiki the nano bot (2007), Betrayer's Moon Tactics (2006), Boson (2006), GPL Arcade Volleyball (2006), GUSANOS (2006), Grabble (2006), JQuest (2006), Linley's Dungeon Crawl (2006), Metal Mech (2006), RPDungeon - computer aided role playing (2006), Rogue Clone IV (2006), StarBlastrix (2006), GalaxyNG (2005), H-World (2005), LinCity (2005), Spice Trade (2005), Tumiki Fighters (2005), WAtomic (2005), Xconq (2005), ZAngband (2005), Colonization too (2004), GM Tools (2004), Gee Whiz (2004), Openglad (2004), PySol (2004), T-Bots (2004), AntiChess (2003), Cat Mother Dead Justice (2003), Civil (2003), Machinations (2003), Pizza Business (2003), Planetary Hoppers (2003), The Clans (2003), Wargamer (2003), XArchon (2003), Maelstrom (2002), Umbra (2002), Greenius' Civil War (2001), Tux Racer (2001), Operation Citadel (2000), xdigger (1999), Ballerburg (1987) Blobby Volley 2 (2017), Eat The Whistle (2017), PokerTH (2017), Scrolling Game Development Kit 2 (2017), Tressette (2017), YSoccer (2017), Zone of Control (2017), 3d.city (2016), Atlantis (2016), Atomic Tanks (2016), Atrinik (2016), Birth of the Empires (2016), DNT (2016), Dark Destiny (2016), Dungeon Monkey Eternal (2016), FreeRCT (2016), GLtron (2016), Infiniminer (2016), JaNaG - Java Name Generator (2016), Mechanized Assault & eXploration Reloaded (2016), Open Yahtzee (2016), Polis (2016), TORCS, The Open Racing Car Simulator (2016), Tremulous (2016), ransack (2016), Aleph One: Marathon (2015), Battleround (2015), Blobwars: Metal Blob Solid (2015), Boulder Dash (2015), D-Fend Reloaded (2015), Dash Engine (2015), DeSmuME (2015), Delta Engine (2015), Egoboo (2015), Free heroes2 engine (2015), Liquid War (2015), M.E.W.L. (2015), Roguish (2015), Scrabble3D (2015), Tenes Empanadas Graciela (2015), The Endless Dungeons (2015), The Epic of Heroes (2015), TwinEngine (2015), Witch Blast (2015), pyORPG (2015), Argentum Online (2014), Battles of Antargis (2014), BlockOut II (2014), Cart Life (2014), Deity (2014), Dune Dynasty (2014), EternalWinterWars (2014), FAR Colony (2014), Fujo (2014), Grobots (2014), Hale (2014), Heroes of Wing Commander (2014), Kingdoms (2014), Lips of Suna (2014), Open RPG Maker (2014), Open Tibia (2014), Radakan (2014), SDL Game Engine 2D (2014), SDL Sopwith (2014), Scorched 3D (2014), Sintel The Game (2014), Summoning Wars (2014), Supremacy (2014), Turious (2014), VDrift (2014), X-Moto (2014), Xenowar (2014), rpge (2014), sandbox Game Maker (2014), Bos Wars (2013), C-evo (2013), Candy Box 2 (2013), Decker (2013), DemiGod (2013), Dungeon Mapper (2013), HoDoKu (2013), Holyspirit (2013), MechCommander 2 Omnitech (2013), Mpango (2013), Murder In The Public Domain (2013), OpenRPG (2013), Phantasy Star Rebirth (2013), Room for Change (2013), SoftPixel Engine (2013), Source of Tales (2013), SpaceZero (2013), The Bub's Brothers (2013), Trinity Reign (2013), Zero Ballistics (2013), Biniax (2012), BlackNova Traders (2012), Blitzkrieg (2012), Cataclysm (2012), Celestron (2012), CommandoJS (2012), Conquests (2012), Dark City (2012), Frozen Bubble (2012), Goblin Camp (2012), Hexwar (2012), Mega Mario (2012), OpenArena (2012), PARPG (2012), TROPHY (2012), Thousand Parsec (2012), Tux Football (2012), UFO2000 (2012), Wizards Magic (2012), Aeron (2011), Battlefield Java (2011), Brain Workshop (2011), Danger from the Deep (2011), Dawn (2011), Fish Fillets - Next Generation (2011), GalaxyMage Redux (2011), KQ Lives (2011), Kobold's Quest 2 (2011), OpenBlox (2011), OpenMOO2 (2011), PyKaraoke (2011), Skrupel - Tribute Compilation (2011), Smash Battle (2011), Song of Albion (2011), Star Control II: The Ur-Quan Masters (2011), Tux of Math Command (2011), UlDunAd (2011), World of Phaos (2011), X-Force: Fight For Destiny (2011), AI Wars (2010), Bombermaaan (2010), Crimson Fields (2010), Dragon History - Dračí Historie (2010), Heroes of Wesnoth (2010), Labyrinth of Worlds (2010), Linwarrior 3D (2010), Mercenary Commander (2010), OpenHoMM (2010), Slay (2010), SpaceTrader for Java (2010), UltraStar (2010), XPilot (2010), XSera (2010), nXtank (2010), Alien Assault Traders (2009), Annchienta (2009), Ardentryst (2009), Battle Tanks (2009), Crown and Cutlass (2009), Dark Oberon (2009), Duel Commander (2009), Fall of Imyrin (2009), Fallen Spire (2009), FreeTrain (2009), Glest (2009), Goblin Hack (2009), MicroWar 2.0 (2009), Netrek (2009), ORIENT (2009), OpenPop (2009), SDL Asylum (2009), Secret Mario Chronicles (2009), Space Trader for Windows (2009), World of Heroes (2009), Yo Frankie! (2009), A Planet's Revenge (2008), Attal: Lords of doom (2008), Avanor (2008), Freestars (2008), Mars, Land of No Mercy (2008), Meritous (2008), Open Game Engine (2008), S.C.O.U.R.G.E. (2008), Sengoku: Warring States of Japan (2008), Space Opera (2008), Armies (2007), Brutal Chess (2007), Eos, Dawn of Light: A Space Opera (2007), Pang Zero (2007), SharpKonquest (2007), Silvertree (2007), Slash'EM (2007), Underworld Adventures (2007), World Builder (2007), kiki the nano bot (2007), Betrayer's Moon Tactics (2006), Boson (2006), GPL Arcade Volleyball (2006), GUSANOS (2006), Grabble (2006), JQuest (2006), Linley's Dungeon Crawl (2006), Metal Mech (2006), RPDungeon - computer aided role playing (2006), Rogue Clone IV (2006), StarBlastrix (2006), GalaxyNG (2005), H-World (2005), LinCity (2005), Spice Trade (2005), Tumiki Fighters (2005), WAtomic (2005), Xconq (2005), ZAngband (2005), Colonization too (2004), GM Tools (2004), Gee Whiz (2004), Openglad (2004), PySol (2004), T-Bots (2004), AntiChess (2003), Cat Mother Dead Justice (2003), Civil (2003), Machinations (2003), Pizza Business (2003), Planetary Hoppers (2003), The Clans (2003), Wargamer (2003), XArchon (2003), Maelstrom (2002), Umbra (2002), Greenius' Civil War (2001), Tux Racer (2001), Operation Citadel (2000), xdigger (1999), Ballerburg (1987)
## Code Languages ## Code Languages
@ -59,23 +59,19 @@ Blobby Volley 2 (2017), Eat The Whistle (2017), PokerTH (2017), Scrolling Game D
## Code licenses ## Code licenses
Without license tag: 6 (1.2%)
Core War, GPL Arcade Volleyball, Imperium, Netrek, Nexiuz, kiki the nano bot
##### Licenses frequency ##### Licenses frequency
- GPL-2.0 (38.0%) - GPL-2.0 (37.5%)
- GPL-3.0 (25.0%) - GPL-3.0 (24.7%)
- MIT (11.0%) - MIT (10.9%)
- Custom (4.5%) - Custom (4.4%)
- ? (3.3%)
- Apache-2.0 (2.5%) - Apache-2.0 (2.5%)
- 3-clause BSD (2.1%) - 3-clause BSD (2.1%)
- ? (2.1%)
- GPL (2.1%) - GPL (2.1%)
- LGPL-2.1 (2.1%) - LGPL-2.1 (2.1%)
- zlib (2.1%) - zlib (2.1%)
- AGPL-3.0 (1.2%) - AGPL-3.0 (1.1%)
- LGPL-3.0 (1.0%) - LGPL-3.0 (1.0%)
- LGPL-2.0 (0.8%) - LGPL-2.0 (0.8%)
- MPL (0.6%) - MPL (0.6%)
@ -104,107 +100,100 @@ Core War, GPL Arcade Volleyball, Imperium, Netrek, Nexiuz, kiki the nano bot
##### Keywords frequency ##### Keywords frequency
- TBS (7.3%) - strategy (19.2%)
- RTS (6.0%) - role playing (13.4%)
- roguelike (6.0%) - action (7.9%)
- remake (5.5%) - framework (6.7%)
- requires original content (4.7%) - simulation (3.8%)
- MMO (4.5%) - turn-based (3.5%)
- online (4.5%) - real time (2.6%)
- shooter (4.5%) - roguelike (2.6%)
- tool (3.7%) - library (2.5%)
- SP (3.1%) - remake (2.4%)
- arcade (2.6%) - puzzle (2.1%)
- 3D (2.4%) - requires original content (2.1%)
- MP (2.1%) - shooter (2.1%)
- racing (2.1%) - massive multiplayer online (1.9%)
- sports (2.1%) - online (1.9%)
- 2D (1.8%) - tool (1.7%)
- board game (1.8%) - adventure (1.6%)
- browser (1.6%) - singleplayer (1.4%)
- puzzle (1.6%) - arcade (1.1%)
- engine (1.3%) - multiplayer (1.1%)
- cards (1.0%) - 2D (1.0%)
- popular (1.0%) - 3D (1.0%)
- visual novel (1.0%) - board game (1.0%)
- TB (0.8%) - racing (0.9%)
- artillery (0.8%) - sports (0.9%)
- console (0.8%) - game engine (0.8%)
- educational (0.8%) - text-based (0.6%)
- karaoke (0.8%) - cards (0.5%)
- music (0.8%) - popular (0.5%)
- port (0.8%) - visual novel (0.5%)
- space (0.8%) - artillery (0.3%)
- boardgame (0.5%)
- chess (0.5%)
- game engine (0.5%)
- kids (0.5%)
- multiplayer (0.5%)
- platform (0.5%)
- programming (0.5%)
- risklike (0.5%)
- sliding blocks (0.5%)
- snake-like (0.5%)
- voxel (0.5%)
- 2d (0.3%)
- JRPG (0.3%)
- MUD (0.3%)
- action-rpg (0.3%)
- action/adventure (0.3%)
- asciiart (0.3%)
- beatem-up (0.3%)
- blocks (0.3%)
- brain exercise (0.3%)
- car (0.3%)
- card game (0.3%)
- cars (0.3%) - cars (0.3%)
- client (0.3%) - console (0.3%)
- continuation of commercial project (0.3%) - educational (0.3%)
- demake (0.3%) - karaoke (0.3%)
- dice game (0.3%) - music (0.3%)
- drive (0.3%) - port (0.3%)
- editor (0.3%) - space (0.3%)
- eingine (0.3%)
- emulator (0.3%)
- engine required (0.3%)
- fly (0.3%)
- football (0.3%)
- for adults (0.3%)
- fps (0.3%)
- frontend (0.3%)
- game editor (0.3%)
- game maker (0.3%)
- game of life (0.3%)
- gui toolkit (0.3%)
- inspired (0.3%)
- interface generator (0.3%)
- isometric (0.3%)
- isometric 2D (0.3%)
- kid-friendly (0.3%)
- language binding (0.3%)
- match 3 (0.3%)
- mmorpg (0.3%)
- physics (0.3%)
- plattformer (0.3%)
- point&click (0.3%)
- poker (0.3%)
- sandbox (0.3%)
- shoot'em up (0.3%)
- shootem up (0.3%)
- side-scrolling (0.3%)
- simulation (0.3%)
- soccer (0.3%)
- social (0.3%)
- source documentation generator (0.3%)
- tank (0.3%)
- tetris attack (0.3%)
- text (0.3%)
- text-based (0.3%)
- top down (0.3%)
- top-down (0.3%)
- tux (0.3%)
- worms-like (0.3%)
- wormslike (0.3%) - wormslike (0.3%)
- chess (0.2%)
- isometric (0.2%)
- kids (0.2%)
- platform (0.2%)
- programming (0.2%)
- risklike (0.2%)
- shoot'em up (0.2%)
- sliding blocks (0.2%)
- snake-like (0.2%)
- top-down (0.2%)
- voxel (0.2%)
- JRPG (0.1%)
- MUD (0.1%)
- action-rpg (0.1%)
- action/adventure (0.1%)
- asciiart (0.1%)
- beat'em up (0.1%)
- blocks (0.1%)
- brain exercise (0.1%)
- card game (0.1%)
- client (0.1%)
- continuation of commercial project (0.1%)
- demake (0.1%)
- dice game (0.1%)
- editor (0.1%)
- eingine (0.1%)
- emulator (0.1%)
- engine required (0.1%)
- fly (0.1%)
- football (0.1%)
- for adults (0.1%)
- frontend (0.1%)
- game editor (0.1%)
- game maker (0.1%)
- game of life (0.1%)
- gui toolkit (0.1%)
- inspired (0.1%)
- interface generator (0.1%)
- kid-friendly (0.1%)
- language binding (0.1%)
- match 3 (0.1%)
- mmorpg (0.1%)
- physics (0.1%)
- plattformer (0.1%)
- point&click (0.1%)
- poker (0.1%)
- ruleset (0.1%)
- sandbox (0.1%)
- side-scrolling (0.1%)
- soccer (0.1%)
- social (0.1%)
- source documentation generator (0.1%)
- tank (0.1%)
- tetris attack (0.1%)
- tux (0.1%)
## Entries without download or play fields ## Entries without download or play fields

View File

@ -22,29 +22,25 @@ valid_fields = ('Home', 'Media', 'State', 'Play', 'Download', 'Platform', 'Keywo
valid_platforms = ('Windows', 'Linux', 'macOS', 'Android', 'Browser') valid_platforms = ('Windows', 'Linux', 'macOS', 'Android', 'Browser')
recommended_keywords = ('action', 'arcade', 'adventure', 'visual novel', 'sports', 'platform', 'puzzle', 'role playing', 'simulation', 'strategy', 'card game', 'board game', 'music', 'educational', 'tool', 'game engine', 'framework', 'library') recommended_keywords = ('action', 'arcade', 'adventure', 'visual novel', 'sports', 'platform', 'puzzle', 'role playing', 'simulation', 'strategy', 'card game', 'board game', 'music', 'educational', 'tool', 'game engine', 'framework', 'library')
def extract_overview_for_toc(file):
def entry_iterator():
""" """
Parses a file for some interesting fields and concatenates the content.
To be displayed after the game name in the category TOCs.
""" """
info = infos[file]
output = [] # get all entries (ignore everything starting with underscore)
entries = os.listdir(games_path)
entries = (x for x in entries if not x.startswith('_'))
if 'code language' in info: # iterate over all entries
output.extend(info['code language']) for entry in entries:
entry_path = os.path.join(games_path, entry)
if 'code license' in info: # read entry
output.extend(info['code license']) content = read_text(entry_path)
# state # yield
if 'state' in info: yield entry, entry_path, content
output.extend(info['state'])
output = ", ".join(output)
return output
def update_readme_and_tocs(infos): def update_readme_and_tocs(infos):
@ -57,7 +53,7 @@ def update_readme_and_tocs(infos):
Needs to be performed regularly. Needs to be performed regularly.
""" """
print('update readme file') print('update readme and toc files')
# delete all toc files # delete all toc files
entries = os.listdir(games_path) entries = os.listdir(games_path)
@ -66,6 +62,7 @@ def update_readme_and_tocs(infos):
os.remove(os.path.join(games_path, entry)) os.remove(os.path.join(games_path, entry))
# read readme # read readme
readme_file = os.path.join(root_path, 'README.md')
readme_text = read_text(readme_file) readme_text = read_text(readme_file)
# compile regex for identifying the building blocks # compile regex for identifying the building blocks
@ -82,14 +79,14 @@ def update_readme_and_tocs(infos):
# create all toc and readme entry # create all toc and readme entry
title = 'All' title = 'All'
file = '_all.md' file = '_all.md'
update = ['- **[{}](games/{})** ({})\n'.format(title, file, len(infos))] update = ['- **[{}](games/{}#{})** ({})\n'.format(title, file, title, len(infos))]
create_toc(title, file, infos) create_toc(title, file, infos)
for keyword in recommended_keywords: for keyword in recommended_keywords:
infos_filtered = [x for x in infos if keyword in x['keywords']] infos_filtered = [x for x in infos if keyword in x['keywords']]
title = keyword.capitalize() title = keyword.capitalize()
file = '_{}.md'.format(keyword) file = '_{}.md'.format(keyword)
update.append('- **[{}](games/{})** ({})\n'.format(title, file, len(infos_filtered))) update.append('- **[{}](games/{}#{})** ({})\n'.format(title, file, title, len(infos_filtered)))
create_toc(title, file, infos_filtered) create_toc(title, file, infos_filtered)
update = ''.join(update) update = ''.join(update)
@ -121,52 +118,8 @@ def create_toc(title, file, entries):
# add to text # add to text
text += '\n'.join(rows) text += '\n'.join(rows)
write_text(toc_file, text)
def update_category_tocs():
"""
Lists all entries in all sub folders and generates the list in the toc file.
Needs to be performed regularly.
"""
# get category paths
category_paths = get_category_paths()
# for each category
for category_path in category_paths:
print('generate toc for {}'.format(os.path.basename(category_path)))
# read toc header line
toc_file = os.path.join(category_path, TOC)
toc_header = read_first_line(toc_file) # stays as is
# get paths of all entries in this category
entry_paths = get_entry_paths(category_path)
# get titles (discarding first two ("# ") and last ("\n") characters)
titles = [read_first_line(path)[2:-1] for path in entry_paths]
# get more interesting info
more = [extract_overview_for_toc(path) for path in entry_paths]
# combine name, file name and more info
info = zip(titles, [os.path.basename(path) for path in entry_paths], more)
# sort according to entry title (should be unique)
info = sorted(info, key=lambda x:x[0])
# assemble output
update = ['- **[{}]({})** ({})\n'.format(*entry) for entry in info]
update = "".join(update)
# combine with toc header
text = toc_header + '\n' + "[comment]: # (start of autogenerated content, do not edit)\n" + update + "\n[comment]: # (end of autogenerated content)"
# write to toc file # write to toc file
with open(toc_file, mode='w', encoding='utf-8') as f: write_text(toc_file, text)
f.write(text)
def check_validity_external_links(): def check_validity_external_links():
@ -174,26 +127,17 @@ def check_validity_external_links():
Checks all external links it can find for validity. Prints those with non OK HTTP responses. Does only need to be run Checks all external links it can find for validity. Prints those with non OK HTTP responses. Does only need to be run
from time to time. from time to time.
""" """
print("check external links (can take a while)")
# regex for finding urls (can be in <> or in () or a whitespace # regex for finding urls (can be in <> or in () or a whitespace
regex = re.compile(r"[\s\n]<(http.+?)>|\]\((http.+?)\)|[\s\n](http[^\s\n,]+)") regex = re.compile(r"[\s\n]<(http.+?)>|\]\((http.+?)\)|[\s\n](http[^\s\n,]+)")
# count # count
number_checked_links = 0 number_checked_links = 0
# get category paths # iterate over all entries
category_paths = get_category_paths() for _, entry_path, content in entry_iterator():
# for each category
for category_path in category_paths:
print('check links for {}'.format(os.path.basename(category_path)))
# get entry paths
entry_paths = get_entry_paths(category_path)
# for each entry
for entry_path in entry_paths:
# read entry
content = read_text(entry_path)
# apply regex # apply regex
matches = regex.findall(content) matches = regex.findall(content)
@ -232,43 +176,36 @@ def check_template_leftovers():
Should be run only occasionally. Should be run only occasionally.
""" """
print('check for template leftovers')
# load template and get all lines # load template and get all lines
text = read_text(os.path.join(games_path, 'template.md')) text = read_text(os.path.join(root_path, 'template.md'))
text = text.split('\n') text = text.split('\n')
check_strings = [x for x in text if x and not x.startswith('##')] check_strings = [x for x in text if x and not x.startswith('##')]
# get category paths # iterate over all entries
category_paths = get_category_paths() for _, entry_path, content in entry_iterator():
# for each category
for category_path in category_paths:
# get paths of all entries in this category
entry_paths = get_entry_paths(category_path)
for entry_path in entry_paths:
# read it line by line
content = read_text(entry_path)
for check_string in check_strings: for check_string in check_strings:
if content.find(check_string) >= 0: if content.find(check_string) >= 0:
print('{}: found {}'.format(os.path.basename(entry_path), check_string)) raise RuntimeError('{}: found {}'.format(os.path.basename(entry_path), check_string))
def fix_keywords(): def fix_keywords():
""" """
Fixes the keywords. Fixes the keywords.
""" """
print('fix keywords')
regex = re.compile(r"(.*)(- Keywords:.*)(- Code repository: .*)", re.DOTALL) regex = re.compile(r"(.*)(- Keywords:.*)(- Code repository: .*)", re.DOTALL)
# get all entries # get all entries (ignore everything starting with underscore)
entries = os.listdir(games_path) entries = os.listdir(games_path)
entries = (x for x in entries if not x.startswith('_'))
# iterate over all entries # iterate over all entries
for entry in entries: for entry, entry_path, content in entry_iterator():
entry_path = os.path.join(games_path, entry)
# read entry
content = read_text(entry_path)
# match with regex # match with regex
matches = regex.findall(content) matches = regex.findall(content)
@ -308,6 +245,7 @@ def fix_keywords():
# write again # write again
write_text(entry_path, new_content) write_text(entry_path, new_content)
def parse_entry(content): def parse_entry(content):
""" """
Returns a dictionary of the features of the content Returns a dictionary of the features of the content
@ -442,19 +380,13 @@ def assemble_infos():
Parses all entries and assembles interesting infos about them. Parses all entries and assembles interesting infos about them.
""" """
print('assemble game infos')
# a database of all important infos about the entries # a database of all important infos about the entries
infos = [] infos = []
# get all entries (ignore everything starting with underscore)
entries = os.listdir(games_path)
entries = (x for x in entries if not x.startswith('_'))
# iterate over all entries # iterate over all entries
for entry in entries: for entry, _, content in entry_iterator():
entry_path = os.path.join(games_path, entry)
# read entry
content = read_text(entry_path)
# parse entry # parse entry
info = parse_entry(content) info = parse_entry(content)
@ -468,35 +400,35 @@ def assemble_infos():
return infos return infos
def generate_statistics(): def update_statistics(infos):
""" """
Generates the statistics page. Generates the statistics page.
Should be done every time the entries change. Should be done every time the entries change.
""" """
# for this function replace infos with infos.values print('update statistics')
infois = infos.values()
# start the page # start the page
statistics_path = os.path.join(games_path, 'statistics.md') statistics_file = os.path.join(root_path, 'statistics.md')
statistics = '[comment]: # (autogenerated content, do not edit)\n# Statistics\n\n' statistics = '[comment]: # (autogenerated content, do not edit)\n# Statistics\n\n'
# total number # total number
number_entries = len(infois) number_entries = len(infos)
rel = lambda x: x / number_entries * 100 # conversion to percent rel = lambda x: x / number_entries * 100 # conversion to percent
statistics += 'analyzed {} entries on {}\n\n'.format(number_entries, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')) statistics += 'analyzed {} entries on {}\n\n'.format(number_entries, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
# State (beta, mature, inactive) # State (beta, mature, inactive)
statistics += '## State\n\n' statistics += '## State\n\n'
number_state_beta = sum(1 for x in infois if 'beta' in x['state']) number_state_beta = sum(1 for x in infos if 'beta' in x['state'])
number_state_mature = sum(1 for x in infois if 'mature' in x['state']) number_state_mature = sum(1 for x in infos if 'mature' in x['state'])
number_inactive = sum(1 for x in infois if 'inactive' in x) number_inactive = sum(1 for x in infos if 'inactive' in x)
statistics += '- mature: {} ({:.1f}%)\n- beta: {} ({:.1f}%)\n- inactive: {} ({:.1f}%)\n\n'.format(number_state_mature, rel(number_state_mature), number_state_beta, rel(number_state_beta), number_inactive, rel(number_inactive)) statistics += '- mature: {} ({:.1f}%)\n- beta: {} ({:.1f}%)\n- inactive: {} ({:.1f}%)\n\n'.format(number_state_mature, rel(number_state_mature), number_state_beta, rel(number_state_beta), number_inactive, rel(number_inactive))
if number_inactive > 0: if number_inactive > 0:
entries_inactive = [(x['title'], x['inactive']) for x in infois if 'inactive' in x] entries_inactive = [(x['title'], x['inactive']) for x in infos if 'inactive' in x]
entries_inactive.sort(key=lambda x: x[0]) # first sort by name entries_inactive.sort(key=lambda x: x[0]) # first sort by name
entries_inactive.sort(key=lambda x: x[1], reverse=True) # then sort by inactive year (more recently first) entries_inactive.sort(key=lambda x: x[1], reverse=True) # then sort by inactive year (more recently first)
entries_inactive = ['{} ({})'.format(*x) for x in entries_inactive] entries_inactive = ['{} ({})'.format(*x) for x in entries_inactive]
@ -517,7 +449,7 @@ def generate_statistics():
# get all languages together # get all languages together
languages = [] languages = []
for info in infois: for info in infos:
if field in info: if field in info:
languages.extend(info[field]) languages.extend(info[field])
@ -533,16 +465,16 @@ def generate_statistics():
field = 'code license' field = 'code license'
# those without license # those without license
number_no_license = sum(1 for x in infois if field not in x) number_no_license = sum(1 for x in infos if field not in x)
if number_no_license > 0: if number_no_license > 0:
statistics += 'Without license tag: {} ({:.1f}%)\n\n'.format(number_no_license, rel(number_no_license)) statistics += 'Without license tag: {} ({:.1f}%)\n\n'.format(number_no_license, rel(number_no_license))
entries_no_license = [x['title'] for x in infois if field not in x] entries_no_license = [x['title'] for x in infos if field not in x]
entries_no_license.sort() entries_no_license.sort()
statistics += ', '.join(entries_no_license) + '\n\n' statistics += ', '.join(entries_no_license) + '\n\n'
# get all licenses together # get all licenses together
licenses = [] licenses = []
for info in infois: for info in infos:
if field in info: if field in info:
licenses.extend(info[field]) licenses.extend(info[field])
@ -559,7 +491,7 @@ def generate_statistics():
# get all keywords together # get all keywords together
keywords = [] keywords = []
for info in infois: for info in infos:
if field in info: if field in info:
keywords.extend(info[field]) keywords.extend(info[field])
@ -574,7 +506,7 @@ def generate_statistics():
statistics += '## Entries without download or play fields\n\n' statistics += '## Entries without download or play fields\n\n'
entries = [] entries = []
for info in infois: for info in infos:
if 'download' not in info and 'play' not in info: if 'download' not in info and 'play' not in info:
entries.append(info['title']) entries.append(info['title'])
entries.sort() entries.sort()
@ -586,7 +518,7 @@ def generate_statistics():
entries = [] entries = []
field = 'code repository' field = 'code repository'
for info in infois: for info in infos:
if field in info: if field in info:
popular = False popular = False
for repo in info[field]: for repo in info[field]:
@ -607,11 +539,11 @@ def generate_statistics():
# get all build systems together # get all build systems together
build_systems = [] build_systems = []
for info in infois: for info in infos:
if field in info: if field in info:
build_systems.extend(info[field]) build_systems.extend(info[field])
statistics += 'Build systems information available for {:.1f}% of all projects.\n\n'.format(len(build_systems) / len(infois) * 100) statistics += 'Build systems information available for {:.1f}% of all projects.\n\n'.format(rel(len(build_systems)))
unique_build_systems = set(build_systems) unique_build_systems = set(build_systems)
unique_build_systems = [(l, build_systems.count(l) / len(build_systems)) for l in unique_build_systems] unique_build_systems = [(l, build_systems.count(l) / len(build_systems)) for l in unique_build_systems]
@ -622,7 +554,7 @@ def generate_statistics():
# C, C++ projects without build system information # C, C++ projects without build system information
c_cpp_project_without_build_system = [] c_cpp_project_without_build_system = []
for info in infois: for info in infos:
if field not in info and ('C' in info['code language'] or 'C++' in info['code language']): if field not in info and ('C' in info['code language'] or 'C++' in info['code language']):
c_cpp_project_without_build_system.append(info['title']) c_cpp_project_without_build_system.append(info['title'])
c_cpp_project_without_build_system.sort() c_cpp_project_without_build_system.sort()
@ -630,7 +562,7 @@ def generate_statistics():
# C, C++ projects with build system information but without CMake as build system # C, C++ projects with build system information but without CMake as build system
c_cpp_project_not_cmake = [] c_cpp_project_not_cmake = []
for info in infois: for info in infos:
if field in info and 'CMake' in info[field] and ('C' in info['code language'] or 'C++' in info['code language']): if field in info and 'CMake' in info[field] and ('C' in info['code language'] or 'C++' in info['code language']):
c_cpp_project_not_cmake.append(info['title']) c_cpp_project_not_cmake.append(info['title'])
c_cpp_project_not_cmake.sort() c_cpp_project_not_cmake.sort()
@ -642,11 +574,11 @@ def generate_statistics():
# get all platforms together # get all platforms together
platforms = [] platforms = []
for info in infois: for info in infos:
if field in info: if field in info:
platforms.extend(info[field]) platforms.extend(info[field])
statistics += 'Platform information available for {:.1f}% of all projects.\n\n'.format(len(platforms) / len(infois) * 100) statistics += 'Platform information available for {:.1f}% of all projects.\n\n'.format(rel(len(platforms)))
unique_platforms = set(platforms) unique_platforms = set(platforms)
unique_platforms = [(l, platforms.count(l) / len(platforms)) for l in unique_platforms] unique_platforms = [(l, platforms.count(l) / len(platforms)) for l in unique_platforms]
@ -655,25 +587,27 @@ def generate_statistics():
unique_platforms = ['- {} ({:.1f}%)'.format(x[0], x[1]*100) for x in unique_platforms] unique_platforms = ['- {} ({:.1f}%)'.format(x[0], x[1]*100) for x in unique_platforms]
statistics += '##### Platforms frequency\n\n' + '\n'.join(unique_platforms) + '\n\n' statistics += '##### Platforms frequency\n\n' + '\n'.join(unique_platforms) + '\n\n'
with open(statistics_path, mode='w', encoding='utf-8') as f: # write to statistics file
f.write(statistics) write_text(statistics_file, statistics)
def export_json(): def export_json(infos):
""" """
Parses all entries, collects interesting info and stores it in a json file suitable for displaying Parses all entries, collects interesting info and stores it in a json file suitable for displaying
with a dynamic table in a browser. with a dynamic table in a browser.
""" """
print('export to json for web display')
# make database out of it # make database out of it
db = {'headings': ['Game', 'Description', 'Download', 'Category', 'State', 'Keywords', 'Source']} db = {'headings': ['Game', 'Description', 'Download', 'State', 'Keywords', 'Source']}
entries = [] entries = []
for info in infos.values(): for info in infos:
# game & description # game & description
entry = ['{} (<a href="{}">home</a>, <a href="{}">entry</a>)'.format(info['title'], info['home'][0], entry = ['{} (<a href="{}">home</a>, <a href="{}">entry</a>)'.format(info['title'], info['home'][0],
r'https://github.com/Trilarion/opensourcegames/blob/master/games/' + info['path']), r'https://github.com/Trilarion/opensourcegames/blob/master/games/' + info['file']),
textwrap.shorten(info['description'], width=60, placeholder='..')] textwrap.shorten(info['description'], width=60, placeholder='..')]
# download # download
@ -683,9 +617,6 @@ def export_json():
else: else:
entry.append('') entry.append('')
# category
entry.append(info['category'])
# state (field state is essential) # state (field state is essential)
entry.append('{} / {}'.format(info['state'][0], 'inactive since {}'.format(info['inactive']) if 'inactive' in info else 'active')) entry.append('{} / {}'.format(info['state'][0], 'inactive since {}'.format(info['inactive']) if 'inactive' in info else 'active'))
@ -780,13 +711,13 @@ def bzr_repo(repo):
return None return None
def update_primary_code_repositories(): def export_primary_code_repositories_json():
primary_repos = {'git':[],'svn':[],'hg':[],'bzr':[]} primary_repos = {'git':[],'svn':[],'hg':[],'bzr':[]}
unconsumed_entries = [] unconsumed_entries = []
# for every entry filter those that are known git repositories (add additional repositories) # for every entry filter those that are known git repositories (add additional repositories)
for info in infos.values(): for info in infos:
field = 'code repository-raw' field = 'code repository-raw'
# if field 'Code repository' is available # if field 'Code repository' is available
if field in info: if field in info:
@ -837,36 +768,37 @@ def update_primary_code_repositories():
primary_repos[k] = sorted(set(v)) primary_repos[k] = sorted(set(v))
# write them to tools/git # write them to tools/git
json_path = os.path.join(games_path, os.path.pardir, 'tools', 'archives.json') json_path = os.path.join(root_path, 'tools', 'archives.json')
text = json.dumps(primary_repos, indent=1) text = json.dumps(primary_repos, indent=1)
write_text(json_path, text) write_text(json_path, text)
if __name__ == "__main__": if __name__ == "__main__":
# paths # paths
games_path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.pardir, 'games')) root_path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.pardir))
readme_file = os.path.realpath(os.path.join(games_path, os.pardir, 'README.md')) games_path = os.path.join(root_path, 'games')
# check for unfilled template lines
check_template_leftovers()
# fix keywords
fix_keywords()
# assemble info # assemble info
infos = assemble_infos() infos = assemble_infos()
# recount and wriite to readme and to tocs # recount and write to readme and to tocs
update_readme_and_tocs(infos) update_readme_and_tocs(infos)
# generate report # generate report
#generate_statistics() update_statistics(infos)
# update database for html table # update database for html table
#export_json() export_json(infos)
# check for unfilled template lines # collect list of primary code repositories
#check_template_leftovers() export_primary_code_repositories_json()
# fix keywords
# fix_keywords()
# check external links (only rarely) # check external links (only rarely)
# check_validity_external_links() # check_validity_external_links()
# collect list of primary code repositories
#update_primary_code_repositories()