Merge tag 'the-great-game_0.1.1'

This commit is contained in:
Simon Brooke 2020-05-24 10:45:23 +01:00
commit b494a89dcb
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
72 changed files with 4129 additions and 369 deletions

2
.gitignore vendored
View file

@ -3,6 +3,7 @@ pom.xml.asc
*.jar
*.class
*.log
*.stl
[0-9a-f]*-init.clj
/lib/
/classes/
@ -17,3 +18,4 @@ pom.xml.asc
.nrepl-port
.cpcache/
*~

81
doc/Baking-the-world.md Normal file
View file

@ -0,0 +1,81 @@
# Baking the world
#### Wednesday, 8 May 2019
![Devogilla's Bridge in Dumfries, early foourteenth century](https://2.bp.blogspot.com/-qxkySlJNmtY/XNKvJksmSjI/AAAAAAAAnXU/z1Zv2LmjydMmi_1q2mWdwVALmdfi9OItwCLcBGAs/s1600/Devorgillas-Bridge.jpg)
In previous posts, I've described algorithms for dynamically [populating](Populating-a-game-world.html) and dynamically [settling](Settling-a-game-world.html) a game world. But at kilometre scale (and I think we need a higher resolution than that - something closer to hectare scale), settling the British Isles using my existing algorithms takes about 24 hours of continuous compute on an eight core, 3GHz machine. You cannot do that every time you launch a new game.
So the game development has to run in four phases: the first three phases happen during development, to create a satisfactory, already populated and settled, initial world for the game to start from. This is particularly necessary if hand-crafted buildings and environments are going to be added to the world; the designers of those buildings and environments have to be able to see the context into which their models must fit.
## Phase one: proving - the procedural world
I'm going to call the initial phase of the game run - the phase which takes place before the quest team write their quests and the art department adds their hand-crafted models - 'proving', as when dough has been been made and set aside to rise.
Then, when the landscape has developed - the areas of forest, scrub, open meadow, moorland, savanah and desert are determined, the rivers plotted, the settlers moved in, their trades determined and their settlements allocated, the roadways which link settlements routed, river crossings and ports defined - the proving process ends, and the world is turned over to the plot-writers, quest builders and designers, for a process we can see as analogous to kneading.
But, before going there, to summarise the proving stage. The inputs are:
1. A raster height map (although this could be randomly generated using any one of many fractal algorithms) - this probably uses ideas from [tessellated multi-layer height map](https://blog.journeyman.cc/2013/07/tessellated-multi-layer-height-map.html);
1. Optionally, a raster rainfall map at 1km resolution (although my personal preference is that this should be generated procedurally from the height map).
The outputs are
1. A vector drainage map (rivers);
1. A raster biome map at roughly 1 km resolution (it might be anything between hectare resolution and 1Km resolution,  but obviously higher resolution takes more storage);
1. A database of settlers and their settlements, such that the settlements have x,y co-ordinates;
1. A vector road map.
In this sense, the 'biome map' is just the end state of a [Microworld](https://blog.journeyman.cc/2014/08/modelling-settlement-with-cellular.html) run. The 'biomes' include things like 'forest', 'scrub', 'heath', 'pasture', but they may also include human settlement, and even settlement by different cultural groups.
This gives us all we need to vegetate and furnish the world. When rendering each square metre we have
1. The x,y coordinates, obviously;
1. The altitude, taken from the height map;
1. The biome, taken from the biome map;
1. The biomes of adjacent cells in the biome map;
1. The proximity of the nearest watercourse;
1. The proximity of the nearest road or pathway;
1. Whether we are inside, or outside, a settlement (where for these purposes, 'settlement' includes enclosed field), and if inside, what type of settlement it is.
Given these parameters, and using the x, y coordinates as seed of a deterministic pseudo-random number generator, we can generate appropriate vegetation and buildings to render a believable world. The reason for pulling adjacent biomes into the renderer is that sharp transitions from one biome to another - especially ones which align to a rectangular grid - rarely exist in nature, and that consequently most transitions from one biome to another should be gradual.
Note that proving, although extremely compute intensive, is not necessarily a one-time job. If the designers aren't satisfied with the first world to emerge from this process, they can run it again, and again, to generate a world with which they are satisfied. It's also possible to hand-edit the output of proving, if needed.
But now, designers and story-writers can see the world in which their creations will be set.
## Phase two: kneading - making the world fit our needs
Enough of proving, let's get on to kneading.
Hand-designed buildings and environments are likely to be needed, or at least useful, for plot; also, particularly, very high status buildings are probably better hand designed. I'm inclined to think that less is more here, for two reasons:
You cannot hand design a very large world, it's just impossible. How CD Project Red managed with Witcher 3 I don't know, since I understand that is largely hand designed; but that was a very large team, and even so it isn't a world on the scale I'm envisaging.
Procedurally generated models take a wee bit of compute power to reify, but not a huge amount, and they're trivial to store - you need one single birch leaf model and one single birch-bark texture generator to make every birch tree in the game, and probably a single parameterised tree function can draw every tree of every species (and quite a lot of shrubs and ground-cover plants, too). But once reified, they take no longer to render than a manually crafted model.
By contrast, a manually crafted model will take a very great deal more space to store, such that being able to render a large world from hand crafted models, without excessive model re-use, isn't going to be possible.
So it's better in my opinion to put effort into good procedural generation functions, not just for foliage but also for buildings. My reason for using a picture of a medieval bridge at the head of the essay is to illustrate exactly this point: even in the medieval period, bridges comprise a series of repeating modules. Take one arch module and one ramp module from Devorgilla's bridge as models, add texture skins for several different stone types, stretch the modules a little in whatever dimension is needed, and repeat the arch module as many times as needed, and you can create a range of bridges to span many different rivers - which will all be visibly similar, but that's fine, that's the nature of a traditional culture - but each slightly different.
Take half a dozen sets of models - timber bridges for forested biomes, brick bridges for biomes without stone or timber - and you can build procedural bridges across a whole continent without ever exactly repeating yourself.
However, in some places the designers and story writers will want, for plot reasons and to create iconic environments, to add models. I'm inclined not to over do this, both for reasons of development effort and for reasons of storage cost, but they will. Very high status buildings may need to be unique and distinctive, for example. These need to be designed and their locations and spatial dimensions added to the database, so that the models can be rendered in the right positions (and, critically, procedurally generated models can be omitted in those positions!)
Story and quest writers will also want characters for their plots. While there's no reason why writers cannot add entirely new characters to the database, there's no reason why they cannot incorporate characters generated in the settlement phase into the story; for this reason, characters need to be able to be tagged in the database as plot characters, and with what quests/elements of the plot they're associated.
This allows a mechanism to prevent a plot character from being killed by another non-player character, or dying of disease or starvation, before the plot elements in which they feature have been completed.
## Phase three: baking - making it delicious
Once the world has been populated, settled, vegetated, the story has been written, the models built, the quests designed, there is probably a process of optimisation - stripping out things which aren't needed at play time, streamlining things that are - before you have a game ready to ship; but really I haven't yet given that much thought.
## Phase four: eating!
At the end, though, you have a game, and a player plays it. How much of the dynamic, organic life that brought the game through proving continues on into the playing phase? If the [gossip](The-spread-of-knowledge-in-a-large-game.html) ideas are to work, if unscripted, non-plot-related events (as well as scripted, plot related events) are to happen while the player plays, if news of these events is to percolate through the world and reach the player in organic, unscripted ways, if a lot of the emergent gameplay I'm imagining is to work, then quite a lot of the dynamic things must be happening.
Of course, part of this depends on the length of 'game world time' is expected to elapse in the course of one play through of the game. If it's less than a year, then you don't need children dynamically being born, and characters dynamically growing older; but if more, then you do. Similarly, you don't need a real simulation of trading to dynamically drive prices in markets, but for a fun trading sub-game to emerge, you probably do, and if you are using merchants as news spreading agents the additional compute cost is not high.
And I understand that many game writers will shudder at the thought that a war might (or might not) start in the middle of their plot, that a battle might, one time in a thousand, take place right where they've plotted some significant encounter. Most modern video games are essentially just very complicated state machines: if you make this sequence of choices, this outcome will happen, guaranteed. Or else they're puddles of random soup, where everything that happens is more or less driven by a random number generator. What I'm envisaging is something quite different: a world in which traders gonna trade, robbers gonna rob, lovers gonna love, scandal-mongers gonna make scandal, organically and dynamically whether the player is there or not, and news of these events will filter through to the player through the gossip network also organically and dynamically.
A world, in short, through which no two runs will ever be the same, in which interesting bits of story will happen with no-one directing or scripting them. And for that to work, some of the same dynamic processes that drove the proving phase have to continue into the eating phase.

114
doc/Pathmaking.md Normal file
View file

@ -0,0 +1,114 @@
# Pathmaking
**NOTE**: this file is called 'pathmaking', not 'pathfinding', because 'pathfinding' has a very specific meaning/usage in game design which is only part of what I want to talk about here.
## Stages in creating routes between locations
### The 'procedural' phase
*see also [[Baking-the-world]]*
Towards the end of the procedural phase of the build process, every agent within the game world must move through the complete range of their needs-driven repertoire. Merchants must traverse their trading routes; soldiers must patrol routes within their employers domain; primary producers and craftspeople must visit the craftspeople who supply them; every character must visit their local inn, and must move daily between their dwelling and their workplace if different; and so on. They must do this over a considerable period - say 365 simulated days.
At the start of the baking phase, routes - roads, tracks and paths - designed by the game designers already exist.
The algorithmic part of choosing a route is the same during this baking phase as in actual game play **except** that during the baking phase the routemap is being dynamically updated, creating a new path or augmenting an existing path wherever any agent goes.
Thus the 'weight' of any section of route is a function of the total number of times that route segment has been traversed by an agent during this baking phase. At the end of the baking phase, routes travelled more than `R` times are rendered as roads, `T` times as tracks, and `P` times as footpaths, where `R`, `T` and `P` are all chosen by the game designer but generally `R > T > P`.
### Algorithmic rules
1. No route may pass through any part of a reserved holding, except the holding which is its origin, if any, and the holding which is its destination (and in any case we won't render paths or roads within holdings, although traversal information may be used to determine whether a holding, or part of it, is paved/cobbled;
2. No route may pass through any building, with the exception of a city gate;
3. We don't have bicycles: going uphill costs work, and you don't get that cost back on the down hill. Indeed, downhills are at least as expensive to traverse as flat ground;
4. Any existing route segment costs only a third as much to traverse as open ground having the same gradient;
5. A more used route costs less to traverse than a less used route.
### River crossings
Crossing rivers is expensive - say five times as expensive as level open ground (but this will probably need tuning). Where a river is shallow enough, (i.e. where the amount of water passing is below some threshold) then a path crossing will be rendered as stepping stones and a track crossing as a ford. Where it's deeper than that, a path crossing either isn't rendered at all or is rendered as a light footbridge. A track or road crossing is rendered as a bridge. However, the maximum length of a bridge varies with the amount of traffic on the route segment, and if the crossing exceeds that length then a ferry is used. Road bridges will be more substantial than track bridges, for example in a biome with both timber and stone available road bridges might be rendered as stone bridges while track bridges were rendered as timber. If the watercourse is marked as `navigable`, the bridge must have a lifting section. It is assumed here that bridges are genetic buildings like most other in-game buildings, and so don't need to be individually designed.
### Representation
At some stage in the future I'll have actual game models to work with and $DEITY knows what the representation of those will be like, but to get this started I need two inputs: a heightmap, from which gradients can be derived, and a route map. The heightmap can conventionally be a monochrome raster image, and that's easy. The route map needs to be a vector representation, and SVG will be as convenient as any. So from the point of view of routing during the baking phase, a route map shall be an SVG with the following classes:
* `exclusion` used on polygons representing e.g. buildings, or impassable terrain which may not be traversed at all;
* `openwater` used on polygons representing oceans and lakes, which may be traversed only by boat (or possibly swimming, for limited distances);
* `watercourse` used on paths representing rivers or streams, with some additional attribute giving rate of flow;
* `navigable` may be an additional class on a path also marked `watercourse` indicating that it is navigable by cargo vessels;
* `route` used on paths representing a path, track or road whose final representation will be dynamically assigned at the end of baking, with some additional attribute giving total traversals to date;
* `path` used on paths representing a path designed by the designers, which will certainly be rendered as a path no matter how frequently it is traversed;
* `track` used on paths representing a track designed by the designers, which will certainly be rendered as a track no matter how frequently it is traversed;
* `road` used on paths representing a road designed by the designers, which will certainly be rendered as a road no matter how (in)frequently it is traversed.
At the end of the baking process the routing engine should be able to write out an updated SVG. New routes should be splined curves, so that they have natural bends not sharp angles.
### The 'Walkmap'
Conventional game pathfinding practice is to divide the traversable area into a mesh of 'convex polygons', where a 'convex polygon' in this sense is, essentially, a polygon having no bays. Routes traverse from a starting point to the centre of a polygon ajacent to the polygon in which the starting point is located. I have reservations as to whether this will do what I need since I'm not convinced it will produce naturalistic paths; however, it's worth at least experimenting with.
There are existing utilities (such as [hmm](https://github.com/fogleman/hmm)) which convert heightmaps into suitable geometry files; however all I've found so far convert to [binary STL](https://en.wikipedia.org/wiki/STL_(file_format)). This isn't a format I find very useful; I'd prefer an XML dialect, and SVG is good enough for me.
`hmm` converts the heightmap into a tesselation of triangles, which are necessarily convex in the sense given above. Utilities (such as [binary-stl-toASCII](https://github.com/IsseiMori/binary-stl-toASCII)) exist to convert binary STL to an ASCII encoded equivalent, which may be easier to parse.
So the pipeline seems to be
1. heightmap to binary STL
2. (optional) binary STL to ASCII STL
3. STL to SVG (where 'SVG' here is shorthand for a convenient vector format)
4. Exclude holdings, buildings, open water, and other exclusions
5. Where we have excluded exclusions, ensure that any non-convex polygons we've created are divided into new convex polygons.
I shall have to write custom code for 4 and 5 above, and, looking at what's available, probably 3 as well.
I'm working on a separate library, [walkmap](https://simon-brooke.github.io/walkmap/), which will attempt to implement this pipeline.
### Pathmaking and scale
Dealing with large heightmaps - doing anything at all with them - is extremely compute intensive. Just converting a 1000x1000 heightmap from STL to SVG is currently taking 8 hours and 15 minutes; and it ends up with a 46 megabyte SVG file! However, most of the time cost is in writing. Reading the STL file takes four and a quarter seconds; converting that STL to SVG in memory takes less than five seconds. So the huge cost is writing the file with Dali.
walkmap.core=> (time (def stl (decode-binary-stl "../the-great-game/resources/maps/heightmap.stl")))
"Elapsed time: 4285.231513 msecs"
#'walkmap.core/stl
walkmap.core=> (time (def svg (stl-to-svg stl)))
"Elapsed time: 4865.798059 msecs"
#'walkmap.core/svg
"Elapsed time: 2.969569560662E7 msecs"
We cannot effectively do routing at metre scale - which is what we ultimately need in settlements - across the entire thousand kilometre square map in one pass. But also we don't need to because much of the continent is by design relatively unpeopled and relatively untracked. The basic concept of the Steppe is that there are two north/south routes, the one over the Midnight Pass into the Great Place and the one via Hans'hua down to the Cities of the Coast, and those can be part of the 'designed roads' map. So we can basically exclude most of the Steppe from routing altogether. We can also - for equally obvious reasons exclude the ocean. The ocean makes up roughly half of the 1000x1000 kilometre map, the steppe and plateau take up half of what's left, mountain massifs eat into the remainder and my feeling is that much of the eastern part of the continent is probably too arid to be settled. So we probably end up only having to dynamically route about 20% of the entire map.
However, this doesn't get round the main problem with scale, and pathmaking. If we pathmake at kilometre scale, then curves will be necessarily very long and sweeping - because each path segment will be at least a kilometre long. And, actually, that's fine for very long distance roads in unpopulated fairly flat territory. It's not so good for long distance roads in rugged terrain, but...
#### Phase one: hand-designed routes
While, given the bottlenecks of the few mountain passes and the one possible pass over the plateau, the caravan routes we want would almost certainly emerge organically out of dynamic routing. But, actually, I know more or less where they need to be and it's probably easiest to hand design them. It will certainly save an enormous amount of brute-force compute time.
I think I have to accept that if I want Alpe d'Huez-style switchbacks up the Sunset and Midnight passes, they're going to have to be hand designed. The same applies to where the Hans'hua caravan road ascends the plateau.
#### Phase two: route segments 'for free' out of settlement activity
If we start by pathmaking around settlements, we can make a first start by giving the template for a holding a segment of track parallel to and just in front of its frontage, and a segment of path along its left hand and rear edges. That, actually, is going to provide 90% of all routing within a settlement, and it's done for us within the [[Settling-a-game-world]] phase.
#### Phase three: metre scale routing around settlements
So if we then collect groups of contiguous 100x100 metre zones each of which has at least one settled holding, we can route at one metre scale over that and what it will essentially do is join up and augment the route segments generated by settlement. Areas of dense settlement do not make up a great deal of the map. Note that experience may show that the metre scale routing is superflous.
#### Phases four, five and six: increasing granularity
Taking the augmented route map comprised of
1. The hand-designed, mainly long distance or plot-important routes;
2. The route segments bordering holdings;
3. The metre scale routing
we can then collect contiguous groups of zones each having at least one holding, where in phase four each zone is a kilometre square and divided into 100x100 grid so that we route at ten metre scale; in phase five we use ten kilometre by ten kilometre zones and we route at 100 metre scale; in phase six, 100 km by 100 km zones and we route at kilometre scale. This process should automatically link up all settlements on the south and west coasts, all those on the north coast, and all in the Great Place; and seeing that the posited pre-designed caravan roads already join the south coast to the north, the north to the Great Place and the Great Place to the south coast, we're done.
At least one of phases three, four, five and six is probably redundant; but without trying I'm not sure which.
### Tidying up
After the full set of increasing-scale passes is complete, we should automatically cull any route segments generated in the settlement phase which have never actually been traversed.
Following that, there may be scope for some final manual tweaking, if desired; I think this is most likely to happen where roads routed at kilometre scale cross rugged terrain.

View file

@ -0,0 +1,112 @@
# Populating a game world
#### Saturday, 6 July 2013
*(You might want to read this essay in conjunction with my older essay, [Settling a game world](../../2009/12/settling-game-world.html), which covers similar ground but which this hopefully advances on)*
For an economy to work people have to be able to move between occupations to fill economic niches. In steady state, non player character (NPC) males become adult as 'vagrants', and then move through the state transitions described in this document. The pattern for females is different.
## Basic occupations
The following are 'unskilled' occupations which form the base of the occupation system. Generally a male character at maturity becomes a 'Vagrant' and wanders though the world until he encounters a condition which allows him to advance up the occupation graph. If an occupation wholly fails, the character can revert to being a 'Vagrant' and start again.
| Occupation | Dwelling | condition | New trade | Notes |
| --- | --- | --- | --- | --- |
| Vagrant | None | land available and animals available | Herdsman | |
| Vagrant | None | arable land available | Farmer | See crops |
| Vagrant | None | has weapons | Outlaw | |
| Herdsman | None | Insufficient food | Vagrant | |
| Farmer | Farm | Insufficient food | Vagrant | |
| Outlaw | None | loses weapons | Vagrant | |
| Vagrant | None | craftsman willing to take on apprentice | Apprentice | |
| Herdsman | None | arable land available | Farmer | |
| Outlaw | None | Battle hardened | OutlawLeader | |
| Apprentice | (craftsman's) | Qualified | Journeyman | |
| Journeyman | None | Unserviced customers available | Craftsman | See crafts |
| Craftsman | See crafts | Too few customers | Journeyman | |
| Journeyman | None | arable land available | Farmer | |
| Vagrant | None | Lord with vacancies available | Soldier | See military |
| OutlawLeader | None | Unprotected farms available | Laird | See nobility |
### Gender dimorphism
In the paragraph above I said 'a male character'. It may seem unfair to create a game world in which the sexual inequality of the real world is carried over, and for that reason it seems sensible that female children should have the same opportunities as male children. But games work on conflicts and injustices, and so it seems reasonable to me to have a completely different occupation graph for women. I haven't yet drawn that up.
### Wandering
Vagrants wander in a fairly random way. While vagrants are wandering they are assumed to live off the land and require no resources. Solitary outlaws similarly wander until they find a leader, although they will avoid the areas protected by nobles. Herdsmen also wander but only over unenclosed pasture. They visit markets, if available, periodically; otherwise, they live off their herds. Journeymen wander from market to market, but are assumed to trade skills with farmers along the way.
## Crafts
Crafts are occupations which require acquired skills. In the initial seeding of the game world there are probably 'pioneers', who are special vagrants who, on encountering the conditions for a particular craft to thrive, instantly become masters of that craft.
| Craft | Dwelling | Supplies | Perishable? | Customer types | Needs market? | Customers | Supplier | Suppliers | Recruits |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| | | | | | | Solo | Per journeyman | Per apprentice | | | |
| | | | | | | --- | --- | --- | | | |
| | | | | | | Min | Max | Min | Max | Min | Max | | | |
| --- | | | | | | --- | --- | --- | --- | --- | --- | | | |
| Smith | Forge | Metal Items | no | Farmer, Soldier | No | 6 | 10 | 4 | 6 | 1 | 3 | Miner | 1 | Vagrant |
| Baker | Bakery | Bread | yes | All NPCs | No | 20 | 30 | 12 | 18 | 6 | 10 | Miller | 1 | Vagrant |
| Miller | Mill | Flour, meal | no | Baker, Innkeeper | No | 2 | 3 | 1 | 2 | 1 | 1 | Farmer | 6 | Vagrant |
| Weaver | Weaver's house | Cloth | no | All NPCs | Yes | 6 | 10 | 4 | 6 | 1 | 3 | Herdsman | 2 | Vagrant |
| Innkeeper | Inn | Food, hospitality | yes | Merhant, Soldier, Farmer, Lord | No | 10 | 20 | 5 | 10 | 2 | 4 | Farmer,Herdsman | 2 | Vagrant |
| Miner | Mine | Ores | no | Smith | Yes | 2 | 3 | 1 | 2 | 1 | 1 | Farmer | 1 | Vagrant |
| Butcher | Butchery | Meat | yes | All NPCs | No | 10 | 20 | 4 | 8 | 2 | 4 | Farmer, Herdsman | 2 | Vagrant |
| Merchant | Townhouse | Transport, logistics | n/a | Craftsmen, nobility | Yes | 10 | 20 | 4 | 8 | 2 | 4 | n/a | n/a | Vagrant |
| Banker | Bank | Financial services | yes | Merchant | Yes | 10 | 20 | 4 | 8 | 2 | 4 | n/a | n/a | Merchant |
| Scholar | Academy | Knowledge | n/a | Ariston, Tyrranos, General, Banker | No | 1 | 4 | 1 | 2 | 0.25 | 0.5 | n/a | n/a | Vagrant |
| Priest | Temple | Religion | n/a | All NPCs | No | 50 | 100 | | | | | | | Scholar |
| Chancellor | Chancellory | Administration | n/a | Ariston, Tyrranos | No | 1 | 1 | 0 | 0 | 0 | 0 | | | Scholar |
| Lawyer | Townhouse | Legal services | n/a | Ariston, Merchant, Banker | No | 4 | 6 | 2 | 3 | 1 | 2 | | | Scholar |
| Magus | Townhouse | Magic | n/a | Tyrranos, General | No | 3 | 4 | 1 | 2 | 0.25 | 0.5 | | | Scholar |
A craftsman starts as an apprentice to a master of the chosen crafts. Most crafts recruit from vagrants, A character must be a journeyman merchant before becoming an apprentice banker, while various intellectual crafts recruit from journeyman scholars.
It's assumed that a journeyman scholar, presented with the opportunity, would prefer to become an apprentice magus than a master scholar.
A journeyman settles and becomes a master when he finds a location with at least the solo/min number of appropriate customer type who are not serviced by another master craftsman of the same craft; he also (obviously) needs to find enough free land to set up his dwelling. The radius within which his serviced customers must live may be a fixed 10Km or it may be variable dependent on craft. If there are unserviced customers within his service radius, the master craftsman may take on apprentices and journeymen to service the additional customers up to a fixed limit perhaps a maximum of four of each, perhaps variable by craft. If the number of customers falls, the master craftsman will first dismiss journeymen, and only in desperate circumstances dismiss apprentices. Every apprentice becomes a journeyman after three years service.
The list of crafts given here is illustrative, not necessarily exhaustive.
## Aristocracy
As in the real world, aristocracy is essentially a protection racket, and all nobles are originally outlaw leaders who found an area with rich pickings and settled down.
| Rank | Follower rank | Client type | Clients protected | Trade in market | Followers per client |
| --- | --- | --- | --- | --- | --- |
| | | | Min | Max | Min | Max | Min | Max |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Bonnet Laird | Private | Farmer | 6 | 20 | 0 | 100 | 0.25 | 0.5 |
| Ariston | Captain | Bonnet Laird | 10 | 30 | 25 | 1000 | 0.5 | 1 |
| Tyrranos | General | Ariston | 10 | unlimited | 250 | unlimited | 0.1 | 0.5 |
Every noble establishes a market and, if he employs a chancellor, taxes trade in it. Crafts which 'need a market' can only be established in the vicinity of a market, irrespective of whether there are sufficient customers elsewhere. All non-perishable goods are traded through the markets, and merchants will transfer surpluses between markets if they can make a profit from it.
My world has essentially three ranks of nobility. The title of the lowest rank will probably change to something vaguely italianate. An aristocrat advances to the next rank when either the requisite number of clients become available in the locality to support the next rank, or the trade in his market becomes sufficient to support the next rank.
Obviously when a province has eleven unprotected bonnet lairds, under the rules given above any of them may become the ariston, and essentially it will be the next one to move after the condition becomes true. If the number of available clients drops below the minimum and the market trade also drops below the minimum, the noble sinks to a lower level in the case of the bonnet laird, to outlaw leader.
## Military
The aristocracy is supported by the military. An outlaw becomes a soldier when his leader becomes a noble. Otherwise, vagrants are recruited as soldiers by bonnet lairds or sergeants who have vacancies. Captains are recruited similarly by aristons or generals, and generals are recruited by tyrranos. If the conditions for employment no longer exist, a soldier is allowed a period of unemployment while he lives off savings and finds another employer, but if no employer is found he will eventually become an outlaw (or, if an officer, an outlaw leader). A private is employed by his sergeant or bonnet laird, a sergeant by his captain, a captain by his arison or general, a general by his tyrranos.
| Rank | Follower rank | Followers | | Condition | New rank |
| --- | --- | --- | --- | --- | --- |
| | | Min | Max | | |
| --- | --- | --- | --- | --- | --- |
| Private | None | 0 | 0 | Battle hardened, unled privates | Sergeant |
| Sergeant | Private | 5 | 15 | More battle hardened, unled sergeantts | Captain |
| Captain | Sergeant | 5 | 15 | More battle hardened, unled captains | General |
| General | Captain | 5 | unlimited | | |
Soldiers have no loyalty to their employer's employer.

View file

@ -0,0 +1,87 @@
# Settling a game world
#### Wednesday, 30 December 2009
*This essay is part of a series with '[Worlds and Flats](Worlds-and-flats.html)' and '[The spread of knowledge in a large game world](The-spread-of-knowledge-in-a-large-game-world.html)'; if you haven't read those you may want to read them before reading this. This essay describes how a large world can come into being and can evolve. I've written again on this subject since - see '[Populating a game world](Populating-a-game-world.html)')*
### Microworld
Some twenty years ago I wrote a rather sophisticated cellular automaton which I called 'Microworld' which modelled the spread of human population over a landscape. It did this by first fractally folding a grid to assign elevations to cells. Then, cells below a critical elevation the tree line were assigned as forest. For each cycle 'year' a cell remained forest, its soil fertility would increase. Random events 'lightning strikes' could change a cell from forest to clearing. Then the following transitions might take place, each with a probability, where each cell is considered to have eight neighbours:
* A forest cell with a lightning strike as a neighbour may catch fire and burn
* A forest cell with a fire as a neighbour may catch fire and burn
* A burning cell become a clearing cell
* A clearing cell with forest or scrub as a neighbour may become scrub
* A scrub cell may become forest
This more or less completes the 'natural' cycle... then we get to settlement. Pastoral and agrarian 1 cells gradually degrade soil fertility (erosion, etc). Agrarian 2 cells do not degrade fertility.
* A clearing cell (including cells above the treeline) may become a pastoral cell (pastoral 1, no settlement)
* A pastoral 1 cell whose soil fertility falls below a threshhold becomes waste
* A pastoral 1 cell with no pastoral neighbours may become waste
* A waste cell below the treeline may become scrub
* A waste cell may become clearing
* A pastoral 1 cell with two or more pastoral neighbours may become a pastoral 2 cell (settlement)
* A forest cell with two or more pastoral neighbours may become clearing
* A pastoral 2 cell with two or more pastoral 2 neighbours may become agrarian 1
* An agrarian 1 cell which falls below a critical fertility becomes pastoral 1
* An agrarian 1 cell with three or more agrarian 1 neighbours becomes agrarian 2 (smith, mill)
* A cell with three or more agrarian 2 neighbours becomes market
* A market cell with no agrarian 2, market or urban neighbours becomes waste
* A cell with two or more market neighbours becomes urban
That's simple, but it provides a remarkable good model of population spread. however, it is essentially a grid and so doesn't make for natural-seeming landscapes when considered as a three dimensional rendered world. How can we do better?
### Microworld Two
The objective of this essay is to outline an angorithm for creating inhabited landscapes in which games can be set, which are satisfyingly believable when rendered in three dimensions. The objective of creating landscapes 'procedurally' that is, with algorithms is that they can be very much larger than designed landscapes for the same richness of local detail. This does not mean that every aspect of the final landscape must be 'procedural'. It would be possible to use the techniques outlined here to create landscapes which were different every time the game was played, but it would be equally possible to create a landscape which was frozen at a particular point and then hand edited to add features useful to the game's plot. And while I'm principally thinking in this about role playing games, this sort of landscape would be applicable to many other sorts of games strategy games, god games, first person shooters...
### The physical geography
Consider our landscape as, once again, a fractally folded sheet on which any given point has characteristics based on its elevation and orientation. There are two critical levels water level and treeline. The water level is, overall, sea level, but in the case of a localised depression it is equal to the lowest land height between the depression and the sea (lakes form in depressions). Computing the fractal sheet forms stage one in computing the landscape. Next, we need functions which, for any given point on the landscape, compute two different dimensions of soil fertility: water and warmth. We'll assume a coriolis prevailing wind blowing from the west, bringing in damp air from an ocean in that direction. Western slopes are wetter than eastern slopes. In principle, also, there's likely to be a rain shadow to the east of high ground leading to considerable aridity, but that may be too expensive to compute. Rain runs swiftly off steeper slopes, more slowly on flatter ground, so flatter ground is wetter than steeper ground. Water flows down hill, so lower ground is on the whole wetter than higher ground. This isn't a precise model of soil hydrology, but I think it's good enough. From each lake a watercourse follows the lowest possible path to the sea. Watercourses modify the land overwhich they flow, carving out a route at least sufficient to carry the amount of water collected in the watershed above each point. Where watercourses flow down steeper gradients, they carve out gullies, possibly with waterfalls. Where they cross shallower gradients or level ground, they become broader. Computing the watercourses becomes the second stage of computing the lanscape.
### Vegetation
Now sprinkle seeds randomly across the landscape at a density of roughly one every ten square metres. Seeds which fall in water, ignore (? or make into water plants?). The position of the plant is taken from the random sprinkling. The species and size of the plant that grows from the plant are a function of the water and warmth functions described above, with latitude and longitude as seeds for pseudo-random functions delivering aspects like branching and so on enough to make individual plants distinct and not carbon copies even of other plants of the same species, but nevertheless recreatable from just the latitude and longitude. So for each plant only two integers need to be stored, yet every time a player passes he will see an identically recreated world. Of course there is a trade-off between storage space and rendering time, and it may be more efficient to build and cache a detailed model of each plant. Like a lot of other things it depends on the game being designed and the processing power of the platform on which that game is delivered. As to how the functions which select the vegetation type work, obviously trees grow better in wetter places, grassland plants in dryer places; within the wetter places, coniferous trees are more prevalent where it is cooler, broadleaves where it is warmer. In the very wettest places, willows, alders and marshland plants. These plants the seeded plants are the feature plants of the landscape. When rendering the landscape the renderer would first apply a suitable local surface texture, for example, in grassland areas, grass.
### Settling the world
So now we need to make this an inhabited landscape. My proposal for this is to introduce proto-actors, which may be the same software agents as the non-player characters the user will interact with (see my essay on the spread of knowledge). At this stage in their lifecycle, the proto-actors are fairly simple state transition machines. Generally, their algorithm is as follows: Starting from one or two seed points, proto-agents will initially move across the landscape travelling at most 20Km in a day, preferring to stop at inns or else at existing settlements; and will maintain a history of the places they have been, never revisiting a place until they have settled. Whenever moving, whether before they have settled or after, proto-actors will plan their route across the landscape, avoiding trees, buildings, and steep gradients, and will prefer to cross rivers at a bridge (if available) or else a ferry (if available), or failing that at the narrowest convenient point. When proto-actors settle, they will claim an area of territory appropriate to their trade more below; the system must build up a database of land holdings. In particular a land holding will never cross a watercourse, an existing road or overlap another land holding (although roads may develop across existing holdings). This is key because I don't want holdings normally to have regular shapes. A settled proto-agent will build a building to live in, and possibly an additional one for his trade. When building buildings, proto-actors will prefer to build at the edge of their land holding, as close as possible to existing buildings and ideally at the side of an existing road. The richer an existing building is, the more attractive it will be to new buildings. Buildings will be built with their long edge aligned with the edge of the owner's hoding.
* A proto-actor is initially, as described above, an itinerant. Itinerants are introduced into the world at a small number of geographical locations, and gradually, not all at once. Itinerants travel as described above. As they move they will leave breadcrumb trails with a roughly ten metre resolution. If they cross an existing track which goes in roughly the right direction they will prefer to follow it. Once a track has been followed by a certain number of proto-actors, it becomes a road.
* An itinerant who finds an area of unsettled grassland of ten hectares with low soil fertility and not more than one hundred trees settles and becomes a pastoralist. He builds a cottage.
* An itinerant who finds an area of unsettled grassland of ten hectares with medium or high soil fertility becomes an agrarian. He builds a homestead. Depending on the fertility of his land he can support between zero and ten labourers, 10% of a smith, 10% of a miller and 10% of a bonnet laird.
* An itinerant who finds an area of unsettled land of 100 square metres within five hundred metres of a homestead with unfulfilled labourer demand becomes a labourer. He builds a cottage.
* An itinerant who finds an area of unsettled land of 100 square metres within five kilometres of ten farmers with unfilled smithing slots becomes a smith. He builds a cottage and a forge.
* An itinerant who finds an area of unsettled land either at the side of a water course or at the top of a hill, and within 5 kilometers of ten farmers with unfilled milling slots becomes a miller. He builds a mill water or wind, appropriate to location.
* Any settler who plays host to more than a certain number of travellers becomes an innkeeper. He claims 400 square metres of unclaimed land as close as possible to his existing settlement and buids an inn and stableyard.
* An itinerant who finds 400 square metres of unclaimed land within a certain distance of an inn and a smith will become a merchant, provided that there are three smiths within a 5Km radius who have unfilled market slots. The merchant builds a marketplace and a counting house.
* An itinerant who finds 200 square metres of unclaimed land within a specified distance of a market with an unfilled chapel slot becomes a priest and builds a chapel and manse, and possibly a school.
* An itinerant who finds 100 square metres of unclaimed land adjacent to where a road crosses a river becomes a ferryman.
* A ferryman who carries more than a certain number of passengers in a given period becomes a tollkeeper and builds a bridge.
This set of rules and possibly others like them (woodcutters, fishermen, hunters...) provide the first wave of settlement. Once the landscape is sufficiently settled by this first wave, there needs to be a period of establishing trading routes. First, every settler will visit his nearest market, leaving a permanent track if there is not already a road. Where several of these tracks overlay one another, once again a road is created. Each merchant then visits each of the ten markets nearest his own, following existing tracks and roads where available. Wherever the merchants do not find roads, new roads are created. This completes the roads network. Each market is now assigned a value which is a function of
* the number of people for whom it is the nearest market
* the sum of the wealth (soil fertility) of the homesteads for which it is the nearest market
* the wealth of other markets within a day's travel
Depending on its wealth a market may support up to twenty stallholders, including bakers, butchers, tanners, weavers, cobblers, chandlers and so on. So a second wave of itinerants sets off. These follow the original rules for itinerants, but if they find an unsettled 100 square metres within five hundred metres of a market, will set up as a stallholder, building a town house and appropriate trade building on their own settlement, and a stall in the market. An itinerant finding a hundred square metres within five hundred metres of a market which has all its stallholder slots filled may become a slum landlord, and build a tenement for day-labourers. Finally, aristocracy. In the second wave an itinerant who finds either a hilltop, an island in a lake or river, or a significant river crossing, with one hectare of unclaimed land and within 5Km of ten farms with unfilled bonnet laird slots becomes a bonnet laird (or 'squire', if you prefer) and builds a fortified house. At the end of the second wave of settlement the ten percent of bonnet lairds with the richest fiefs (using much the same metric as for the wealth of markets) become barons and build castles.
### Rendering the buildings
This seems to me to provide an algorithmic means of settling a landscape which will generate organic and satisfying patterns of settlement. But it all fails if the buildings are chosen from a limited palette of models. As with the trees I think we need algorithmic mechanisms of building similar-but-different buildings which can be repeatably rendered from relatively small data sets. As an example of what I mean, in damper landscapes where wood is likely to be available, there might be a higher probability of stave buildings, or weatherboarding, with mainly shingle roofs. In slightly less damp areas where timber is still available, cruck frames and half timbered buildings will prevail, with mostly thatched roofs. In the dryest areas, cob and brick buildings will be common, often with tile roofs. On steeper hillsides, stone buildings will be common, perhaps with slate roofs. Within each of these types there are essential cells from which a building is created. These cells can be longer or shorter, taller or lower, wider or narrower. A building may comprise a single cell, or more. If more than three cells they may be arranged in a row or round a courtyard. And they may have one story or two. Which they have can be based like the details of the plants on functions which take latitude and longitude as arguments and which, internally use pseudo-randoms seeded from those latitude and longitude values.
### How vast a world?
OK, so, with this general approach, how big can we go? The answer seems to me to be 'big enough'. A 32 bit integer gives somewhat over four billion values, so can resolve down to one millimetre precision in a world 4000 kilometres by 4000 kilometres. But we don't actually need millimetre resolution; centimetre would be quite small enough. And that gives us potential for a world 40000Km square, or 1.6 billion square kilometres, which is three times the surface area of planet Earth.
In practice we can't go that big for all sorts of space and time reasons. Recording land heights is inevitably an issue. I don't know of a pseudo random function which will generate satisfying land heights. Anything based on Julia sets, for example, ends up with landforms symmetrical around a central point. Furthermore, the shapes of fractals which can be constructed from simple functions tend to have a noticable and unnatural degree of self-similarity across scales. I'd dearly like to be wrong on this, but I think we need to store at minimum elevation values at ten metre intervals. If we can accept 100mm resolution for elevations, storing 16 bit values gives a range of 6,500 metres - 21,000 feet - from the deepest seabed to the peaks of the highest mountains.
This means that landform information alone requires 20Kbytes per square kilometre - unindexed, but seeing it's a rigid ten metre grid that isn't a problem. Which, in turn, means that you can store landform information for a planet the size of Earth in one terrabyte. But we don't need a planet the size of earth. Scotland is 80,000 square kilometers of land area; allowing for a bit of sea around to the horizon around it, say 100,000 square kilometers. That seems to me more than big enough to be a game space. It amounts to 160Mb of landform data, which is completely manageable.
If we stored plant data for every distinctive plant in Scotland - even at one per ten square metres - that does become an impractically large data set, because quite apart from anything else, the plant locations do have to be indexed. But actually, given that the actual plants that grow are a function of the location at which they grow, no player is going to notice if the pattern of the locations of plants is the same for each square kilometre. So we can manage plant data for a land area the size of Scotland in 400,000 bytes - we could do it in less (if the locations were generated using a pseudo-random function, much less).
Building data is different. We need to store the latitude, longitude and type of every building explicitly, and again they need to be indexed in order that we can recover the buildings in a given area efficiently. We need about 16 bytes per building (four bytes latitude, four longitude, two type; then for each tile a null-terminated vector of pointers to building records). If we assume that our feudal land of 80,000 square kilometers has a population of a million, and that there are on average five occupants of every building, that's two hundred thousand buildings, implying 3.2Mb of data.
Of course, that's just the backing store size. As tiles are loaded into active memory - see the essay 'Tiles and Flats' this raw backing data has to be inflated procedurally into actual models that can be rendered; models which may have thousands of vertices and hundreds of kilobytes of textures. The functions which do that inflating have some finite size, and, significantly, they'll need to work on prototype models which will in turn take storage space. Finally there are hand-edited models potentially used at particular plot locations; those need to be stored more or less in full. But all this has not become an unmanageable amount of data. It seems to me plausible that you could store a fully populated 100,000 square kilometer game world on one uncompressed 700Mb CD. On a 4Gb DVD, you could do it very easily.

View file

@ -0,0 +1,86 @@
# The spread of knowledge in a large game world
#### Saturday, 26 April 2008
![part of the role of Dandelion, in The Witcher games, is to provide the player with news](https://4.bp.blogspot.com/-F2gxx0dRM8o/UlfSsRe8ybI/AAAAAAAAYIA/I1I9D5Yk7to/s1600/Tw2_full_Dandelion.png)
### Note
_This version of this essay has been adapted to use the code in `the-great-game.gossip.news-items`, [q.v.](the-great-game.gossip.news-items.html). The original version of the essay is [still available on my blog](https://blog.journeyman.cc/2008/04/the-spread-of-knowledge-in-large-game.html)._
These days we have television, and news. But in a late bronze age world there are no broadcast media. News spreads by word of mouth. If non-player characters are to respond effectively to events in the world, knowledge has to spread.
How to model this?
Some non-player characters - doesn't need to be many - are news-spreaders. News-spreaders need to travel. They have to travel even when there are no player characters in the vicinity. But, they don't have to travel very often - once or twice every game day. When a news-spreader is in the immediate vicinity of another character, the pair may (with some degree of randomness) exchange news. There needs to be a hierarchy in the exchange of news, so that 'I-saw' events need to be more likely to be passed on than 'I-heard' events; there needs to be a counter which counts the number of times a knowledge item has been passed on, and also an age counter so that knowledge items are less likely to be passed on as they get older.
One obvious class of news-spreader is a merchant. Merchant agents can either shuttle mechanically between a fixed group of markets or else possibly respond intelligently to supply and demand. Provided that there is a mesh of merchant routes covering the markets of the game world, and that a useful subset of non-merchant characters are required to visit a market every few game days, this should give a reasonably realistic framework for news spreading.
What else? What things qualify as news items? I think at least the following:
* Deaths of sentient characters, especially if violent
* Commodity prices
* Changes of rulers in cities
* Marriages of sentient characters
* Plot events, flagged as events by the game designer
Obviously, news is more valuable if the people involved are important or notorious: the significance of a story is probably the product of the significance of the people concerned.
So a news item becomes a map with keys similar to
[:verb :actor :other :location :nth-hand :time-stamp]
The [exact keys for each verb are specified here](the-great-game.gossip.news-items.html#var-news-topics).
for example
{:verb :kill,
:actor {:id :fred :name "Fred"},
:other {:id :joe :name "Joe"},
:location [{45467 78613} :hanshua :plateau],
:nth-hand 3,
:time-stamp 17946463}
meaning 'I spoke to a man who'd spoken to a man who said he saw fred kill joe at the game time represented by the time stamp 17946463, at the coordinates {45467 78613} in Hans'hua on the Plateau'. Obviously, the non-player character must be able to construct a natural language sentence from the tuple when speaking within the hearing of a player character, but there's no need for a non-player character to produce a natural language sentence for another non-player character to parse; instead they can just exchange tuples.
But if we're exchanging knowledge between agents, then agents must have a means of representing knowledge. This knowledge is an association between subjects and sets of statement, such that when the agent learns the statement
{:verb :kill,
:actor {:id :fred :name "Fred"},
:other {:id :joe :name "Joe"},
:location [{45467 78613} :hanshua :plateau],
:nth-hand 3,
:time-stamp 17946463}
it adds this statement (with the 2 incremented to 3) to the set of statements it knows about fred and also to the set of statements it knows about joe. It's possible that the receiving agent could then challenge for further statements about fred and/or joe, the automated equivalent of a 'who's joe?' question.
There could be feedback in this. Fred's and joe's significance scores could be incremented for each character to whom the statement is passed on, increasing the likeliness that fred, at least, would feature in more news stories in future. There needs also to be some means of managing how the non-player character's attitude to the subjects of the statement are affected. For example, If fred kills joe, and the character (say bill) receiving the news feels positively towards joe, then bill's attitude to fred should become sharply more hostile. If bill feels neutral about joe, then bill's attitude to fred should still become a bit more hostile, since killing people is on the whole a bad thing. But it bill feels very hostile towards joe, then bill's attitude to fred should become more friendly.
But also, the added knowledge is *degraded*. If the recipient isn't from Hans'hua, the exact location isn't meaningful to them, for example. If the recipient isn't interested in Joe, precisely who was killed may be forgotten. So what is stored could become:
{:verb :kill,
:actor {:id :fred :name "Fred"},
:location [:hanshua :plateau],
:nth-hand 4,
:time-stamp 17946463}
The timestamp could also be degraded, or lost altother - although how exactly this is represnted I'm not certain. Someone interested in the incident may remember 'it was exactly 17 days ago', whereas someone else may remember that it was 'this month, I think'.
Obviously the rate of decay, and the degree of randomness, of the news-passing algorithm would need to be tuned, but this schema seems to me to describe a system with the following features:
* Non-player characters can respond to questions about significant things which happen in the world - without it all having to be scripted
* If you travel fast enough, you can keep ahead of your notoriety
* Characters on major trade routes will know more about what is happening in the world than characters in backwaters
This seems to me a reasonably good model of news spread.
### Scaling of the algorithm
Let's work around the idea that a 'game day' equates to about two hours of wall clock time. Let's work around the idea that there are of the order of fifty markets in the game world, and that for each market there are two or three merchants whose 'home base' it is.
Obviously non-player characters who are within the vicinity of a player character have to be 'awake', in order that the player can see them interacting with their world and can interact with them. Those characters have to be in working memory and have to be in the action polling loop in any case. So there's no extra cost to their gossiping away between each other - around the player there's a moving bubble of gossip, allowing each character the player interacts with to have a high probability of having some recent news.
But the merchants who aren't in the vicinity of a player don't have to be in working memory all the time. Each merchant simply requires to be 'woken up' - loaded into memory - once per game day, move a day's journey in one hop, and then, if arriving at an inn or at a market, wake and exchange news with one resident character - an innkeeper or a gossip. So the cost of this algorithm in a fifty-market game is at worst the cost of loading and unloading two non-player characters from memory every minute, and copying two or three statements from the knowledge set of one to the knowledge set of the other. If you're dynamically modifying significance scores, of course, you'd need to also load the characters about whom news was being passed on; but this still doesn't seem unduly onerous.
Obviously, if memory is not too constrained it may be possible to maintain all the merchants, all the innkeepers and all the characters currently being talked about in memory all the time, further reducing the cost.

View file

@ -0,0 +1,90 @@
# Voice acting considered harmful
#### Wednesday, 25 February 2015
![The Witcher: Conversation with Kalkstein](https://3.bp.blogspot.com/-ZI90HLjEcuo/VO4f-yXP3sI/AAAAAAAAZt4/C0hQ7hScWyM/s1600/witcher_conversation.jpg)
Long, long, time ago, I can still remember when... we played (and wrote) adventure games where the user typed at a command line, and the system printed back at them. A Read-Eval-Print loop in the classic Lisp sense, and I wrote my adventure games in Lisp. I used the same opportunistic parser whether the developer was building the game
Create a new room north of here called dungeon-3 the player was playing the game
Pick up the rusty sword and go north or the player was talking to a non-player character
Say to the wizard 'can you tell me the way to the castle' Of course, the parser didn't 'understand' English. It worked on trees of words, in which terminal nodes were actions and branching nodes were key words, and it had the property that any word it didn't recognise at that point in sentence was a noise word and could be ignored. A few special hacks (such as 'the', 'a', or 'an' was an indicator that what came next was probably a noun phrase, and thus that if there was more than one sword in the player's immediate environment the one that was wanted was the one tagged with the adjective 'rusty'), and you ended up with a parser that most of the time convincingly interpreted most of what the player threw at it.
Text adventures fell into desuetude partly because they weren't graphic, but mainly because people didn't find typing natural, or became dissatisfied with the repertoire of their parsers. Trying to find exactly the right combination tokens to persuade the game to carry out some simple action is not 'fun', it's just frustrating, and it turned people off. Which is a shame because just at the time when people were abandoning text adventures we were beginning to have command parsers which were actually pretty good. Mine, I think, were good - you could have a pretty natural conversation with them, and in 'building' mode, when it hit a 'sorry I don't understand' point, it allowed you to input a path of keywords and a Lisp function so that in future it would understand.
So much, so [Eliza](http://www.csee.umbc.edu/courses/331/papers/eliza.html).
Modern role-playing games - the evolutionary successors of those high and far off text adventures - don't have text input. Instead, at each stage in a conversation, the user is offered a choice of three or four canned responses, and can pick one; very often what the player's character actually says then differs from the text the user has chosen, often with differences of nuance which the user feels (s)he didn't intend. And the non-player-character's response is similarly canned. Indeed, the vast majority of non-player characters in most games have a 'repertoire', if one may call it that, of only one sentence. Others will have one shallow conversational tree, addressing one minor quest or plot-point.
If you want to talk to them about anything else - well, you just can't.
Only a very few key non-player characters will have a large repertoire of conversational trees, relevant to all parts of the plot. And even those trees are not deep. You soon exhaust them; the characters' ability to simulate real agency just isn't there.
I first wrote about the limiting effects of voice acting in [my review of the original Witcher game](../../2008/02/the-witcher-story-telling-of-high-order.html), back in 2008; things haven't got better.
## On phones: speaking
In my pocket I carry a phone. It's not big: 127 x 64.9 x 8.6mm. A small thing.
When I first used Android phones for navigation, I used to delight in their pronunciation of Scots placenames - pronouncing them phonetically, as spelled, and as though their spelling were modern English. What's delightful about Scots placenames is that they are linguistically and orthographically so varied - their components may be Brythonic, Goidaelic, Anglian, Norn, French, English, or even Latin; and very frequently they combine elements of more than one language (Benlaw Hill, anyone? Derrywoodwachy?).
Yes, gentle reader, this does seem a long way from game design; be patient, I'm getting there. But I'm going to digress even further for first...
There have been orthographic changes, and pronunciation changes consequent on orthographic changes. For example, medieval Scots used the letter [Yogh](http://en.wikipedia.org/wiki/Yogh) (ȝ), which isn't present in the English alphabet. So when Edinburgh printers in the early modern period bought type for their printing presses from England, there was no Yogh in the font. So they substituted Zed. So we get names like Dalȝiel, Kirkgunȝeon, Menȝies, Cockenȝie. How do you pronounce them?
The letter that looks like a 'z' is pronounced rather like a 'y'; so
* Deeyell
* Kirkgunyeon
* Mingis
and... drumroll...
* Cockenzie.
What happened?
Well, Dalȝiel and Menȝies are personal names, and people are protective of their own names. Kirkgunȝeon is a small, unimportant place, and all the locals know how it is pronounced. Scots folk, are, after all, used to Scots orthography and its peculiarities. So those names haven't changed.
But at Cockenȝie, another small, unimportant place, a nuclear power station was built. The nuclear power station was built by people (mostly) from England, who didn't know about Yogh or the peculiarities of Scots orthography - and were possibly too arrogant to care. So they called it 'Cockenzie'. And as there were so many more of them and they had so much higher status than the locals, their name stuck, and nowadays even local people mostly say 'Cockenzie', as though it were spelled with a Zed. Because, of course, it is spelled with a Zed. Because, as any British schoolchild knows, there's no Yogh in the alphabet.
Except, of course, when there is.
Another more interesting example of the same thing is '[Kirkcudbright](http://www.journeyman.cc/placenames/place?id=153)'. It's a town built around the kirk (church) of saint Cuthbert. So how does it come to have a 'd' in it? And why is it pronounced 'Kirkoobry'? Well, the venerable Cuthbert pronounced his name in a way which would be represented in modern English as 'Coothbrecht', but he spelled it 'Cuðbrecht'. See that 'ð'? That's not a 'd', it's an Eth. Because Cuðbrecht was Anglian, and the Anglian alphabet had [Eth](http://en.wikipedia.org/wiki/Eth); it's pronounced as a soft 'th', and Icelandic still has it (as well as Thorn, þ, a hard 'th' sound). Medieval scribes didn't know about Eth, so in copying out ð they wrote the more familiar d. The local people, however, mostly couldn't read, so the pronunciation of the name didn't change with the change in spelling (although the pronunciation, too, has drifted a little with time).
So, in brief, pronouncing Scots placenames is hard, and there are a lot of curious rules, and consequently it's not surprising that five years ago, listening to Android's pronunciation of Scots placenames was really funny.
But what's really curious is that now it isn't. Now, it rarely makes a mistake. Now, Android can do text to speech on unusual and perverse orthography, and get it right better than 95% of the time - and manage a reasonably natural speaking voice while doing so. On a small, low power machine which fits in my pocket.
## On phones: listening
But navigation is not all I can do with my phone. I can also dictate. By which I don't mean I can make a voice recording, play it back later and type what I hear, although, of course, I can. I mean I can dictate, for example, an email, and see it in text on my phone before I send it. It quickly learned my peculiarities of diction, and it now needs very little correction. On a small, low power machine which fits in my pocket.
## And breathe
Right, so where am I going with all this? Well, we interact with modern computer role playing games through very restricted, entirely scripted dialogues. Why do we do so? Why, on our modern machines with huge amounts of store, do our non-player characters - and worse still, our player character, our own avatar - have such restricted repertoires?
Because they are voice acted. Don't get me wrong, voice acting makes a game far more engaging. But for voice acting to work, the people doing the acting have to know not only the full range of sentences that their character is going to speak, but also roughly how they feel (angry? sad? excited?) when they say it. Ten years ago, voice acting was probably the only way you could have got this immediacy into games, because ten years ago, text-to-speech systems were pretty crude - think of Stephen Hawking's voice synthesiser. But now, Edinburgh University's [open source synthesiser](http://www.cstr.ed.ac.uk/projects/festival/morevoices.html) is pretty good, and comes with twenty-four voices (and seeing it's open source, you can of course add your own). Speech to text was probably better ten years ago - think of [Dragon Naturally Speaking](http://en.wikipedia.org/wiki/Dragon_NaturallySpeaking) - but it was proprietary software, and used a fair proportion of a machine's horsepower. Now there's (among others) Carnegie Mellon's open source [Sphinx](http://cmusphinx.sourceforge.net/) engine, which can quickly adapt to your voice.
So, we have text-to-speech engines which can generate from samples of many different voices, and speech to text engines which can easily be tuned to your particular voice. There's even a program called [Voice Attack](http://www.voiceattack.com/), built on top of Microsoft's proprietary speech to text engine, which already allows you to [control games with speech](https://www.youtube.com/watch?v=8dnJ--pSjdE). Where does that take us?
Well, we already know how to make sophisticated natural language parsers for text, given moderately limited domains - we don't need full natural language comprehension here.
## You may think it's a long way down the road to the chemist
There are things one needs to know in a game world. For example: I need a sword, where's the nearest swordsmith? In a real quasi-medieval world, certainly every soldier would be able to tell you, and everyone from the swordsmith's town or village. Very celebrated swordsmiths would be known more widely.
And the thing is, the game engine knows where the nearest swordsmith is. It knows what potion will heal what wound, and what herbs and what tincture to use to make it. It knows which meats are good to eat, and which inns have rooms free. It knows good campsites. It knows where there be dragons. It knows where the treasure is hid. It knows - as far as the game and its plot are concerned - everything.
So to make an in-game Siri - an omniscient companion you could ask anything of - would be easy. Trivial. It also wouldn't add verisimilitude to the game. But to model which non-player characters know what is not that much harder. Local people know what's where in their locality. Merchants know the prices in nearby markets. They, and minstrels, know the game-world's news - major events that affect the plot. Apothecaries, alchemists and witches know the properties of herbs and minerals.
And to model which non-player characters are friendly, and willing to answer your every question; which neutral or busy, and liable to answer tersely; and which actively hostile, and likely, if they answer at all, to deliberately mislead - that's not very much harder.
I'm not arguing that voice acting, and scripted dialogue trees, should be done away with altogether. They still have a use, as cutscenes do, to advance plot. And I'm not suggesting that we use voice to control the player characters movements and actions - I'm not not suggesting that we should say 'run north; attack the troll with the rusty sword'. Keyboards and mice may be awkward ways to control action, but they're better than that. Bur I am suggesting that one should be able to talk to any (supposedly sentient) character in the game, and have them talk reasonably sensibly back. As one can already do physically in wandering an open world, a full voice interaction system would allow one to go off piste - to leave the limited, constrained pre-scripted interaction of the voice-acted dialogue tree. And that has got to make our worlds, and our interactions with them, richer, more surprising, more engaging.
A hybrid system needn't be hard to achieve, needn't be jarring in use. You can record the phonemes of your voice actor's voice, so that the same character will have roughly the same voice - the same timbre, the same vowel sounds, the same characteristics of  pronunciation - whether in a voice acted dialogue or in a generated one.
We don't need to let voice acting limit the repertoires of our characters any more. And we shouldn't.

View file

@ -1,6 +1,6 @@
# Game world economy
Broadly this essay extends ideas presented in [Populating a game world](https://blog.journeyman.cc/2013/07/populating-game-world.html), q.v.
Broadly this essay extends ideas presented in [Populating a game world](Populating-a-game-world.html), q.v.
## Primary producers

View file

@ -19,7 +19,7 @@ that I need to be able to use it to tell stories, in order to create initial
threads of narrative from which players can start their exploration.
Note that, by 'conflict', here, I explicitly do not mean 'killing people',
or even 'killing non-player characters'. I have [written extensively](https://blog.journeyman.cc/2015/02/voice-acting-considered-harmful.html)
or even 'killing non-player characters'. I have [written extensively](Voice-acting-considered-harmful.html)
about the problem in many current video games that all too often the only
way of interacting with non-player characters is to kill them. Killing
people should be one of the potential ways of resolving conflicts, because
@ -32,18 +32,18 @@ repertoire of speech.
## Previous essays that are relevant
* [The spread of knowledge in a large game world](https://blog.journeyman.cc/2008/04/the-spread-of-knowledge-in-large-game.html) (2008) discusses what individual non-player characters know, and how to model dynamic updates to their knowledge;
* [The spread of knowledge in a large game world](The-spread-of-knowledge-in-a-large-game-world.html) (2008) discusses what individual non-player characters know, and how to model dynamic updates to their knowledge;
* [Settling a game world](https://blog.journeyman.cc/2009/12/settling-game-world.html) (2009) gives rough outline of ideas about creating the environment, including modelling things like soil fertility, local building materials, and consequently local architecture;
* [Tessellated multi-layer height map](https://blog.journeyman.cc/2013/07/tessellated-multi-layer-height-map.html) (2013) gives ideas for how a designed geography for a very large world could be stored relatively economically;
* [Genetic Buildings](https://blog.journeyman.cc/2013/07/genetic-buildings.html) (2013) sketches algorithms which would allow procedurally-generated buildings to be site-appropriate, broadly variable and reproducable;
* [Populating a game world](https://blog.journeyman.cc/2013/07/populating-game-world.html) (2013) provides outline algorithms for how a world can be populated, and how organic mixes of trades and crafts can be modelled;
* [Populating a game world](Populating-a-game-world.html) (2013) provides outline algorithms for how a world can be populated, and how organic mixes of trades and crafts can be modelled;
* [Modelling the change from rural to urban](https://blog.journeyman.cc/2013/07/modelling-change-from-rural-to-urban.html) (2013) describes the idea of procedurally modelling settlements, but it is grid-based and not particularly satisfactory and has largely been superceded in my thinking;
* [Of pigeons, and long distance messaging in a game world]() (2013) builds on ideas about flows of information;
* [Of pigeons, and long distance messaging in a game world](https://blog.journeyman.cc/2013/10/of-pigeons-and-long-distance-messaging.html) (2013) builds on ideas about flows of information;
* [Modelling rural to urban, take two](https://blog.journeyman.cc/2013/10/modelling-rural-to-urban-take-two.html) (2013) revisited the idea of modelling organic settlement structures, trying to find algorithms which would naturally produce more persuasive settlement models, including further ideas on the procedural generation of buildings;
* [More on modelling rivers](https://blog.journeyman.cc/2014/09/more-on-modelling-rivers.html) (2014) talks about modelling hydrology, with implications for soil fertility;
* [Modelling settlement with cellular automata](https://blog.journeyman.cc/2014/08/modelling-settlement-with-cellular.html) (2014) talks about successful implementation of algorithms to model vegetative environment, human settlement and the impact of human settlement on the environment;
* [Voice acting considered harmful](https://blog.journeyman.cc/2015/02/voice-acting-considered-harmful.html) (2015) outlines the ideas behind full speech interaction with non-player characters, and modelling what those non-player characters should be able to speak about;
* [Baking the world](https://blog.journeyman.cc/2019/05/baking-world.html) (2019) an outline of the overall process of creating a world.
* [Baking the world](Baking-the-world.html) (2019) an outline of the overall process of creating a world.
## Organic and emergent game-play
@ -183,8 +183,8 @@ easy:
So each agent is assigned - by the dreaded random number generator - one top
level goal when they are instantiated. I don't think it's necessary to model
change of top level goals, although of course that does happen in real life;
however, although each agent has one top level goal, they will have lower l
evel 'stretch goals' also taken from this list: so at each decision point in
however, although each agent has one top level goal, they will have lower
level 'stretch goals' also taken from this list: so at each decision point in
an agent's planning loop, if base level needs are satisfied and progress on
the top level goal is blocked, actions should be chosen which progress one
of the lower goals. Indeed, it's possible that all agents could have all
@ -379,4 +379,4 @@ Each game day, every habitual traveller within the 'local' gossip bubble
exchanges some items of gossip with the nearest innkeeper to their current
location. In the second and third gossip bubbles, it's probably only more
favoured gossip agents who do this. See
[The spread of knowledge in a large game world](https://blog.journeyman.cc/2008/04/the-spread-of-knowledge-in-large-game.html)
[The spread of knowledge in a large game world](The-spread-of-knowledge-in-a-large-game-world.html)

View file

@ -2,7 +2,7 @@
In a dynamic pre-firearms world with many small states and contested regions, trade is not going to be straightforward. Not only will different routes have different physical characteristics - more or less mountainous, more or fewer unbridged river crossings - they will also have different political characteristics: more of less taxed, more or less effectively policed.
Raids by outlaws are expected to be part of the game economy. News of raids are the sort of things which may propagate through the [[gossip]] system. So are changes in taxation regime. Obviously, knowledge items can affect merchants' trading strategy; in existing prototype code, individual merchants already each keep their own cache of known historical prices, and exchange historical price data with one another; and use this price data to select trades to make.
Raids by outlaws are expected to be part of the game economy. News of raids are the sort of things which may propagate through the [gossip](the-great-game.gossip.gossip.html) system. So are changes in taxation regime. Obviously, knowledge items can affect merchants' trading strategy; in existing prototype code, individual merchants already each keep their own cache of known historical prices, and exchange historical price data with one another; and use this price data to select trades to make.
So: to what extent is it worth modelling the spread of knowledge of trade cost and risk?

View file

@ -0,0 +1,34 @@
# Naming of Characters
Generally speaking, in modern RPGs, every character with any impact on the plot has a distinct name. But if we are going to give all non-player characters sufficient agency to impact on the plot, then we must have a way of naming tens or hundreds of thousands of characters, and distinct names will become problematic (even if we're procedurally generating names, which we shall have to do. So this note is about how characters are named.
The full name of each character will be made up as follows:
[epithet] [clan] [personal-name] the [trade-or-rank] of [location], son/daughter of [parent]
Based on, roughly, historical name patterns like
Archibald (personal-name) the Grim (epithet), Earl (trade-or-rank) of Douglas (location)
Where
1. *epithet* is a prefix based on some notable feature or feat of the character. Most characters won't have an epithet, unless they have some notable feature or they've done something notable. If a character does something notable in the course of the game, they will subsequently gain an epithet; 'notability' may be measured by how many times the event is transmitted through the gossip network.
2. *clan* is special to the Western Clans, although people from the Great Place may possible use the name of their house similarly.
3. *personal-name* is chosen from one of a limited set of limited sets; different cultural groups will have different (possibly overlapping) sets of names, but within each set there will only be a limited subset
4. *trade-or-rank* is just that. "Smith", "Miller", "Ariston", "Captain". Either only master craftsfolk have the trade-or-rank name of their craft, or we distinguish between 'Calon the Smith', who may be a journeyman, and 'Calon the Master Smith', who is a master.
5. *location* is the name of a location; a village, town, city or province. The location which forms part of a character's name is the location where there current home is, not the location where they were born or where their ancestors came from
Full names will almost never be used - only, perhaps, in extremely formal circumstances. The form of a name used will depend on context, and will generally be just sufficient to disambiguate the character in the context.
If the speaker is in Sinhua and referring to someone from Sinhua, they won't refer to them as 'of Sinhua'.
If everyone present is a bargee and the speaker referring to someone who is also a bargee, they won't refer to them as 'the bargee'.
The question asked influences the context: in answer to the question 'who is the best sword smith', the answer will not be 'Calon the Smith' but 'Calon of Sinhua'.
Patronymics/matronymics will not normally be used of adults (although they may be used of apprentices and journeymen.

13
doc/on-dying.md Normal file
View file

@ -0,0 +1,13 @@
# On Dying
Death is the end of your story. One of the tropes in games which, for me, most breaks immersion is when you lose a fight and are presented with a screen that says 'you are dead. Do you want to reload your last save?' Life is not like that. We do not have save-states. We die.
So how could this be better handled?
You lose a fight. Switch to cutscene: the battlefield, after the fight, your body is there. Probably no sound. A party of non-enemies crosses the battlefield and finds your body. We see surprise and concern. They gather around you. Cut to interior scene, you are in a bed, unconcious, being tended; cut to similar interior scene, you are in a bed, conscious, being tended; cut to exterior scene, you are sitting with some of your saviours, and the game restarts.
Time has passed; events in the game world have moved on. You can talk to your saviours about it. You have lost a lot of strength, and most of the gear you were carrying. You must do whatever it is you do within the game mechanics to rebuild strength, and to acquire more gear. Significantly you have acquired a debt of honour to your saviours, which they may call on later. You almost certainly have new scars, and might possibly have some lasting effects (although how that interacts with other game mechanics might be tricky).
So who are the non-enemies? It depends on context. If you have a party, and some of that party survived the fight, it's your party. Otherwise, if you're in a populated place, it's locals. If it's on a road or other route, it's passing merchants. If you're in the wilderness, a hunting party. It's a bunch of non-hostiles who might reasonably be expected to be around: that's what matters. It's about not breaking immersion.
Obviously losing a fight must have weight, it must have meaning, it must have in-game consequences; otherwise it is meaningless.

47
doc/orgnic-quests.md Normal file
View file

@ -0,0 +1,47 @@
# Organic Quests
The structure of a modern Role Playing Came revolves around 'quests': tasks that the player character is invited to do, either by the framing narrative of the game or by some non-player character ('the Quest Giver'). Normally there is one core quest which provides the overarching narrative for the whole game. [Wikipedia](https://en.wikipedia.org/wiki/Quest_(gaming)) offers a typology of quests as follows:
1. Kill quests
2. Combo quests
3. Delivery quests
4. Gather quests
5. Escort quests
6. Syntax quests
7. Hybrids
'Gather quests' are more frequently referred to in the literature as 'fetch quests', and 'kill quests' are simply a specialised form of fetch quest where the item to be fetched is a trophy of the kill. A delivery quest is a sort of reverse fetch quest: instead of going to some location or NPC and getting a specific item to return to the quest giver, the player is tasked to take a specific item from the quest giver to some location or NPC.
Hybrids are in effect chains of quests: do this task in order to get this precondition of this other task, in order to get the overall objective; obviously such chains can be deep and involved - the 'main quest' of every role playing game I know of is a chain or hybrid quest.
My understanding is that what Wikipedia means by a 'syntax quest' is what one would normally call a puzzle.
An escort quest is typically a request to take a specified non-player character safely through a dangerous area.
Combo quests are not, in my opinion, particularly relevant to the sorts of game we're discussing here.
So essentially quests break down into three core types
1. Fetch and deliver quests
2. Escort quests
3. Puzzles
which are combined together into more or less complex chains, where the simplest chain is a single quest.
Given that quests are as simple as this, it's obvious that narrative sophistication is required to make them interesting; and this point is clearly made by some variants of roguelike games which procedurally generate quests: they're generally pretty dull. By contrast, the Witcher series is full of fetch-quests which are made to really matter by being wrapped in interesting character interaction and narrative plausibility. Very often this takes the form of tragedy: as one reviewer pointed out, the missing relatives that Geralt is asked to find generally turn out to be (horribly) dead. In other words, creative scripting tends to deliver much more narratively satisfying quests than is usually delivered by procedural generation.
But, if we're thinking of a game with much more intelligent non-player characters with much more conversational repertoir, as I am, can satisfying quests emerge organically? In space trading games such as [Elite](https://www.telegraph.co.uk/games/11051122/Elite-the-game-that-changed-the-world.html), a primary activity is moving goods from markets with surplus (and thus low prices) to markets with shortage (and thus high prices). This is, in effect, a game made up of deliver quests - but rather than deliver quests which are scripted, they are deliver quests which arise organically out of the structure of the game world.
I already have working code for non-player character merchants, who move goods from city to city based on market information available to them. For player characters to join in this trading is an organic activity emerging from the structure of the world, which provides an activity. But moving merchants provides a market opportunity for bandits, who can intercept and steal cargoes, and so for mercenaries, who can protect cargoes from bandits, and so on. And because I have an architecture that allows non-player characters to fill economic niches, there will be non-player characters in all these niches.
Where a non-player character can act, so can a player character: when a (non-player character) merchant seeks to hire a caravan guard and a player character responds, that's an organic escort quest.
The key idea behind organic quests is that the circumstance and requirments for quests emerges as an emergent behaviour out of the mechanics of the game world. A non-player character doesn't know that there is a player character who is different from them; rather, when a non-player character needs something they can't readily achieve for themselves, they will ask other characters to help, and that may include the player character.
This means, of course, that characters need a goal-seeking planning algorithm to decide their actions, with one option in any plan being 'ask for help'. Thus, 'asking for help' becomes a mechanism within the game, a normal behaviour. Ideally non-player characters will keep track of quite complex webs of loyalty and of obligation - debts of honour, duties of hospitality, collective loyalties. So that, if you do a favour for some character in the world, that character's tribe, friends, obligation circle, whatever, are now more likely to do favours for you.
Obviously, this doesn't stop you doing jobs you get directly paid/rewarded for, but I'd like the web of obligation to be at least potentially much richer than just tit for tat.
Related to this notion is the notion that, if you are asked to do a task by a character and you do it well, whether for pay or as a favour, your reputation for being competent in tasks of that kind will improve and the more likely it is that other characters will ask you to do similar tasks; and this will apply to virtually anything another character can ask of you in the game world, from carrying out an assassination to delivering a message to finding a quantiy of some specific commodity to having sex.
So quests can emerge organically from the mechanics of the world and be richly varied; I'm confident that will work. What I'm not confident of is that they can be narratively satisfying. This relates directly to the generation of speech.

View file

@ -57,10 +57,10 @@ But nevertheless, in The Witcher 3, a decision was made to pack incident fairly
An in-game day doesn't have to be as long as a wall clock day, and, indeed, typically isn't. But nevertheless, doing several game days of incident-free travel, even in beautiful scenery, is not going to be engaging - which implies a fast-travel mechanic.
I don't like fast travel, I find it a too-obvious breaking of immersion. Also, of course, one of the interesting things about a game in a merchant/outlaw ecosystem is the risk of interception on a journey. The Dragon Age series handled interrupted travel in 'fast travel' by randomly interacting the loading screen you get when moving from location to location in Dragon Age's patchwork worlds by dumping you into a tiny arena with enemies. That's really, really bad - there's no other way to say this. Everything about it shouts artifice.
I don't like fast travel, I find it a too-obvious breaking of immersion. Also, of course, one of the interesting things about a game in a merchant/outlaw ecosystem is the risk of interception on a journey. The Dragon Age series handled interrupted travel in 'fast travel' by randomly interrupting the loading screen you get when moving from location to location in Dragon Age's patchwork worlds by dumping you into a tiny arena with enemies. That's really, really bad - there's no other way to say this. Everything about it shouts artifice.
So I'm thinking of a different mechanism: one I'm calling cruise control.
You set out on a task which will take a long time - such as a journey, but also such as any routine task. You're shown either a 'fast forward' of your character carrying out this task, or a series of cinematic 'shots along the way'. This depends, of course, on their being continuous renderable landscape between your departure and your destination, but there will be. This fast-forward proceeds at a substantially higher time gearing than normal game time - ten times as fast perhaps; we need it to, because as well as doing backgound scenery loading to move from one location to another, we're also simulating lots of non-player agents actions in parts of the world where the player currently isn't. So a 'jump cut' from one location to another isn't going to work anyway.
You set out on a task which will take a long time - such as a journey, but also such as any routine task. You're shown either a 'fast forward' of your character carrying out this task, or a series of cinematic 'shots along the way'. This depends, of course, on there being continuous renderable landscape between your departure and your destination, but there will be. This fast-forward proceeds at a substantially higher time gearing than normal game time - ten times as fast perhaps; we need it to, because as well as doing backgound scenery loading to move from one location to another, we're also simulating lots of non-player agents' actions in parts of the world where the player currently isn't. So a 'jump cut' from one location to another isn't going to work anyway.
The player can interrupt 'fast forward' at any time. But also, the game itself may bring you out of fast forward when it anticipates that there may be action which requires decision - for example, when there are outlaws in the vicinity. And it will do this **before** the player's party is under immediate attack - the player will have time to take stock of the situation and prepare appropriately. Finally, this will take place in the full open world; the player will have the option to choose *not* to enter the narrow defile, for example, to ask local people (if there are any) for any news of outlaw activity, or, if they are available, to send forward scouts.

View file

@ -15,19 +15,15 @@
<td class="with-number">Total</td><td class="with-number">Blank</td><td class="with-number">Instrumented</td>
</tr></thead>
<tr>
<td><a href="the_great_game/core.clj.html">the-great-game.core</a></td><td class="with-bar"><div class="covered"
style="width:33.333333333333336%;
float:left;"> 2 </div><div class="not-covered"
style="width:66.66666666666667%;
float:left;"> 4 </div></td>
<td class="with-number">33.33 %</td>
<td><a href="the_great_game/agent/agent.clj.html">the-great-game.agent.agent</a></td><td class="with-bar"><div class="covered"
style="width:100.0%;
float:left;"> 2 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-bar"><div class="covered"
style="width:66.66666666666667%;
float:left;"> 2 </div><div class="not-covered"
style="width:33.333333333333336%;
float:left;"> 1 </div></td>
<td class="with-number">66.67 %</td>
<td class="with-number">6</td><td class="with-number">1</td><td class="with-number">3</td>
style="width:100.0%;
float:left;"> 2 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-number">43</td><td class="with-number">5</td><td class="with-number">2</td>
</tr>
<tr>
<td><a href="the_great_game/gossip/gossip.clj.html">the-great-game.gossip.gossip</a></td><td class="with-bar"><div class="covered"
@ -42,41 +38,58 @@
style="width:87.17948717948718%;
float:left;"> 34 </div></td>
<td class="with-number">12.82 %</td>
<td class="with-number">65</td><td class="with-number">5</td><td class="with-number">39</td>
<td class="with-number">66</td><td class="with-number">5</td><td class="with-number">39</td>
</tr>
<tr>
<td><a href="the_great_game/gossip/news_items.clj.html">the-great-game.gossip.news-items</a></td><td class="with-bar"><div class="covered"
style="width:92.73422562141491%;
float:left;"> 485 </div><div class="not-covered"
style="width:7.265774378585086%;
float:left;"> 38 </div></td>
<td class="with-number">92.73 %</td>
<td class="with-bar"><div class="covered"
style="width:88.07339449541284%;
float:left;"> 96 </div><div class="partial"
style="width:7.339449541284404%;
float:left;"> 8 </div><div class="not-covered"
style="width:4.587155963302752%;
float:left;"> 5 </div></td>
<td class="with-number">95.41 %</td>
<td class="with-number">256</td><td class="with-number">31</td><td class="with-number">109</td>
</tr>
<tr>
<td><a href="the_great_game/merchants/markets.clj.html">the-great-game.merchants.markets</a></td><td class="with-bar"><div class="covered"
style="width:75.11737089201878%;
float:left;"> 160 </div><div class="not-covered"
style="width:24.88262910798122%;
float:left;"> 53 </div></td>
<td class="with-number">75.12 %</td>
style="width:96.71361502347418%;
float:left;"> 206 </div><div class="not-covered"
style="width:3.2863849765258215%;
float:left;"> 7 </div></td>
<td class="with-number">96.71 %</td>
<td class="with-bar"><div class="covered"
style="width:56.81818181818182%;
float:left;"> 25 </div><div class="partial"
style="width:11.363636363636363%;
float:left;"> 5 </div><div class="not-covered"
style="width:31.818181818181817%;
float:left;"> 14 </div></td>
<td class="with-number">68.18 %</td>
style="width:93.18181818181819%;
float:left;"> 41 </div><div class="partial"
style="width:4.545454545454546%;
float:left;"> 2 </div><div class="not-covered"
style="width:2.272727272727273%;
float:left;"> 1 </div></td>
<td class="with-number">97.73 %</td>
<td class="with-number">84</td><td class="with-number">8</td><td class="with-number">44</td>
</tr>
<tr>
<td><a href="the_great_game/merchants/merchant_utils.clj.html">the-great-game.merchants.merchant-utils</a></td><td class="with-bar"><div class="covered"
style="width:46.017699115044245%;
float:left;"> 104 </div><div class="not-covered"
style="width:53.982300884955755%;
float:left;"> 122 </div></td>
<td class="with-number">46.02 %</td>
style="width:66.12377850162866%;
float:left;"> 203 </div><div class="not-covered"
style="width:33.876221498371336%;
float:left;"> 104 </div></td>
<td class="with-number">66.12 %</td>
<td class="with-bar"><div class="covered"
style="width:54.09836065573771%;
float:left;"> 33 </div><div class="partial"
style="width:4.918032786885246%;
float:left;"> 3 </div><div class="not-covered"
style="width:40.98360655737705%;
float:left;"> 25 </div></td>
<td class="with-number">59.02 %</td>
<td class="with-number">92</td><td class="with-number">7</td><td class="with-number">61</td>
style="width:66.66666666666667%;
float:left;"> 48 </div><div class="partial"
style="width:5.555555555555555%;
float:left;"> 4 </div><div class="not-covered"
style="width:27.77777777777778%;
float:left;"> 20 </div></td>
<td class="with-number">72.22 %</td>
<td class="with-number">106</td><td class="with-number">7</td><td class="with-number">72</td>
</tr>
<tr>
<td><a href="the_great_game/merchants/merchants.clj.html">the-great-game.merchants.merchants</a></td><td class="with-bar"><div class="covered"
@ -125,6 +138,47 @@
<td class="with-number">4.03 %</td>
<td class="with-number">173</td><td class="with-number">6</td><td class="with-number">124</td>
</tr>
<tr>
<td><a href="the_great_game/objects/container.clj.html">the-great-game.objects.container</a></td><td class="with-bar"><div class="covered"
style="width:100.0%;
float:left;"> 2 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-bar"><div class="covered"
style="width:100.0%;
float:left;"> 2 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-number">11</td><td class="with-number">1</td><td class="with-number">2</td>
</tr>
<tr>
<td><a href="the_great_game/objects/game_object.clj.html">the-great-game.objects.game-object</a></td><td class="with-bar"><div class="covered"
style="width:60.0%;
float:left;"> 3 </div><div class="not-covered"
style="width:40.0%;
float:left;"> 2 </div></td>
<td class="with-number">60.00 %</td>
<td class="with-bar"><div class="covered"
style="width:60.0%;
float:left;"> 3 </div><div class="not-covered"
style="width:40.0%;
float:left;"> 2 </div></td>
<td class="with-number">60.00 %</td>
<td class="with-number">19</td><td class="with-number">2</td><td class="with-number">5</td>
</tr>
<tr>
<td><a href="the_great_game/time.clj.html">the-great-game.time</a></td><td class="with-bar"><div class="covered"
style="width:99.62121212121212%;
float:left;"> 263 </div><div class="not-covered"
style="width:0.3787878787878788%;
float:left;"> 1 </div></td>
<td class="with-number">99.62 %</td>
<td class="with-bar"><div class="covered"
style="width:98.33333333333333%;
float:left;"> 59 </div><div class="partial"
style="width:1.6666666666666667%;
float:left;"> 1 </div></td>
<td class="with-number">100.00 %</td>
<td class="with-number">144</td><td class="with-number">21</td><td class="with-number">60</td>
</tr>
<tr>
<td><a href="the_great_game/utils.clj.html">the-great-game.utils</a></td><td class="with-bar"><div class="covered"
style="width:100.0%;
@ -136,6 +190,23 @@
<td class="with-number">100.00 %</td>
<td class="with-number">35</td><td class="with-number">3</td><td class="with-number">19</td>
</tr>
<tr>
<td><a href="the_great_game/world/location.clj.html">the-great-game.world.location</a></td><td class="with-bar"><div class="covered"
style="width:88.37209302325581%;
float:left;"> 76 </div><div class="not-covered"
style="width:11.627906976744185%;
float:left;"> 10 </div></td>
<td class="with-number">88.37 %</td>
<td class="with-bar"><div class="covered"
style="width:70.58823529411765%;
float:left;"> 12 </div><div class="partial"
style="width:17.647058823529413%;
float:left;"> 3 </div><div class="not-covered"
style="width:11.764705882352942%;
float:left;"> 2 </div></td>
<td class="with-number">88.24 %</td>
<td class="with-number">37</td><td class="with-number">4</td><td class="with-number">17</td>
</tr>
<tr>
<td><a href="the_great_game/world/routes.clj.html">the-great-game.world.routes</a></td><td class="with-bar"><div class="covered"
style="width:99.21875%;
@ -183,9 +254,9 @@
</tr>
<tr><td>Totals:</td>
<td class="with-bar"></td>
<td class="with-number">51.99 %</td>
<td class="with-number">66.90 %</td>
<td class="with-bar"></td>
<td class="with-number">54.62 %</td>
<td class="with-number">68.74 %</td>
</tr>
</table>
</body>

View file

@ -0,0 +1,137 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../../coverage.css"/> <title> the_great_game/agent/agent.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;the-great-game.agent.agent
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&quot;Anything&nbsp;in&nbsp;the&nbsp;game&nbsp;world&nbsp;with&nbsp;agency&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[the-great-game.objects.game-object&nbsp;:refer&nbsp;[ProtoObject]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[the-great-game.objects.container&nbsp;:refer&nbsp;[ProtoContainer]]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
005&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;;;&nbsp;&nbsp;hierarchy&nbsp;of&nbsp;needs&nbsp;probably&nbsp;gets&nbsp;implemented&nbsp;here
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;;;&nbsp;&nbsp;I&#x27;m&nbsp;probably&nbsp;going&nbsp;to&nbsp;want&nbsp;to&nbsp;defprotocol&nbsp;stuff,&nbsp;to&nbsp;define&nbsp;the&nbsp;hierarchy
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;;;&nbsp;&nbsp;of&nbsp;things&nbsp;in&nbsp;the&nbsp;gameworld;&nbsp;either&nbsp;that&nbsp;or&nbsp;drop&nbsp;to&nbsp;Java,&nbsp;wich&nbsp;I&#x27;d&nbsp;rather&nbsp;not&nbsp;do.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
009&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
010&nbsp;&nbsp;(defprotocol&nbsp;ProtoAgent
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;&nbsp;&nbsp;&quot;An&nbsp;object&nbsp;which&nbsp;can&nbsp;act&nbsp;in&nbsp;the&nbsp;world&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;&nbsp;&nbsp;(act
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[actor&nbsp;world&nbsp;circle]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;Allow&nbsp;`actor`&nbsp;to&nbsp;do&nbsp;something&nbsp;in&nbsp;this&nbsp;`world`,&nbsp;in&nbsp;the&nbsp;context&nbsp;of&nbsp;this
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`circle`;&nbsp;return&nbsp;the&nbsp;new&nbsp;state&nbsp;of&nbsp;the&nbsp;actor&nbsp;if&nbsp;something&nbsp;was&nbsp;done,&nbsp;`nil`
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;nothing&nbsp;was&nbsp;done.&nbsp;Circle&nbsp;is&nbsp;expected&nbsp;to&nbsp;be&nbsp;one&nbsp;of
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
017&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`:active`&nbsp;-&nbsp;actors&nbsp;within&nbsp;visual&#x2F;audible&nbsp;range&nbsp;of&nbsp;the&nbsp;player
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;character;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`:pending`&nbsp;-&nbsp;actors&nbsp;not&nbsp;in&nbsp;the&nbsp;active&nbsp;circle,&nbsp;but&nbsp;sufficiently&nbsp;close
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;it&nbsp;that&nbsp;they&nbsp;may&nbsp;enter&nbsp;the&nbsp;active&nbsp;circle&nbsp;within&nbsp;a&nbsp;short&nbsp;period;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`:background`&nbsp;-&nbsp;actors&nbsp;who&nbsp;are&nbsp;active&nbsp;in&nbsp;the&nbsp;background&nbsp;in&nbsp;order&nbsp;to
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handle&nbsp;trade,&nbsp;news,&nbsp;et&nbsp;cetera;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`other`&nbsp;-&nbsp;actors&nbsp;who&nbsp;are&nbsp;not&nbsp;members&nbsp;of&nbsp;any&nbsp;other&nbsp;circle,&nbsp;although
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;I&#x27;m&nbsp;not&nbsp;clear&nbsp;whether&nbsp;it&nbsp;would&nbsp;ever&nbsp;be&nbsp;appropriate&nbsp;to&nbsp;invoke&nbsp;an
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`act`&nbsp;method&nbsp;on&nbsp;them.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
027&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;`act`&nbsp;method&nbsp;*must&nbsp;not*&nbsp;have&nbsp;side&nbsp;effects;&nbsp;it&nbsp;must&nbsp;*only*&nbsp;return&nbsp;a
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;state.&nbsp;If&nbsp;the&nbsp;actor&#x27;s&nbsp;intention&nbsp;is&nbsp;to&nbsp;seek&nbsp;to&nbsp;change&nbsp;the&nbsp;state&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;something&nbsp;else&nbsp;in&nbsp;the&nbsp;game&nbsp;world,&nbsp;it&nbsp;must&nbsp;add&nbsp;a&nbsp;representation&nbsp;of&nbsp;that
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;intention&nbsp;to&nbsp;the&nbsp;sequence&nbsp;which&nbsp;will&nbsp;be&nbsp;returned&nbsp;by&nbsp;its
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`pending-intentions`&nbsp;method.&quot;)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;(pending-intentions
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
034&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[actor]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;Returns&nbsp;a&nbsp;sequence&nbsp;of&nbsp;effects&nbsp;an&nbsp;actor&nbsp;intends,&nbsp;as&nbsp;a&nbsp;consequence&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
036&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acting.&nbsp;The&nbsp;encoding&nbsp;of&nbsp;these&nbsp;is&nbsp;not&nbsp;yet&nbsp;defined.&quot;))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
037&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
038&nbsp;&nbsp;;;&nbsp;(defrecord&nbsp;Agent
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
039&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;&quot;A&nbsp;default&nbsp;agent.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
040&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;ProtoObject
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
041&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;ProtoContainer
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
042&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;ProtoAgent
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
043&nbsp;&nbsp;;;&nbsp;)
</span><br/>
</body>
</html>

View file

@ -8,196 +8,199 @@
001&nbsp;&nbsp;(ns&nbsp;the-great-game.gossip.gossip
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&quot;Interchange&nbsp;of&nbsp;news&nbsp;events&nbsp;between&nbsp;agents&nbsp;agents&quot;
002&nbsp;&nbsp;&nbsp;&nbsp;&quot;Interchange&nbsp;of&nbsp;news&nbsp;events&nbsp;between&nbsp;gossip&nbsp;agents&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[the-great-game.utils&nbsp;:refer&nbsp;[deep-merge]]))
003&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[the-great-game.utils&nbsp;:refer&nbsp;[deep-merge]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[the-great-game.gossip.news-items&nbsp;:refer&nbsp;[learn-news-item]]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
004&nbsp;&nbsp;
005&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;;;&nbsp;Note&nbsp;that&nbsp;habitual&nbsp;travellers&nbsp;are&nbsp;all&nbsp;gossip&nbsp;agents;&nbsp;specifically,&nbsp;at&nbsp;this
006&nbsp;&nbsp;;;&nbsp;Note&nbsp;that&nbsp;habitual&nbsp;travellers&nbsp;are&nbsp;all&nbsp;gossip&nbsp;agents;&nbsp;specifically,&nbsp;at&nbsp;this
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;;;&nbsp;stage,&nbsp;that&nbsp;means&nbsp;merchants.&nbsp;When&nbsp;merchants&nbsp;are&nbsp;moved&nbsp;we&nbsp;also&nbsp;need&nbsp;to
007&nbsp;&nbsp;;;&nbsp;stage,&nbsp;that&nbsp;means&nbsp;merchants.&nbsp;When&nbsp;merchants&nbsp;are&nbsp;moved&nbsp;we&nbsp;also&nbsp;need&nbsp;to
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;;;&nbsp;update&nbsp;the&nbsp;location&nbsp;of&nbsp;the&nbsp;gossip&nbsp;with&nbsp;the&nbsp;same&nbsp;key.
008&nbsp;&nbsp;;;&nbsp;update&nbsp;the&nbsp;location&nbsp;of&nbsp;the&nbsp;gossip&nbsp;with&nbsp;the&nbsp;same&nbsp;key.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
008&nbsp;&nbsp;
009&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
009&nbsp;&nbsp;(defn&nbsp;dialogue
010&nbsp;&nbsp;(defn&nbsp;dialogue
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;&nbsp;&nbsp;&quot;Dialogue&nbsp;between&nbsp;an&nbsp;`enquirer`&nbsp;and&nbsp;an&nbsp;`agent`&nbsp;in&nbsp;this&nbsp;`world`;&nbsp;returns&nbsp;a
011&nbsp;&nbsp;&nbsp;&nbsp;&quot;Dialogue&nbsp;between&nbsp;an&nbsp;`enquirer`&nbsp;and&nbsp;an&nbsp;`agent`&nbsp;in&nbsp;this&nbsp;`world`;&nbsp;returns&nbsp;a
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;&nbsp;&nbsp;map&nbsp;identical&nbsp;to&nbsp;`enquirer`&nbsp;except&nbsp;that&nbsp;its&nbsp;`:gossip`&nbsp;collection&nbsp;may&nbsp;have
012&nbsp;&nbsp;&nbsp;&nbsp;map&nbsp;identical&nbsp;to&nbsp;`enquirer`&nbsp;except&nbsp;that&nbsp;its&nbsp;`:gossip`&nbsp;collection&nbsp;may&nbsp;have
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;&nbsp;&nbsp;additional&nbsp;entries.&quot;
013&nbsp;&nbsp;&nbsp;&nbsp;additional&nbsp;entries.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;not&nbsp;yet&nbsp;written,&nbsp;this&nbsp;is&nbsp;a&nbsp;stub.
014&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;not&nbsp;yet&nbsp;written,&nbsp;this&nbsp;is&nbsp;a&nbsp;stub.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;&nbsp;&nbsp;[enquirer&nbsp;respondent&nbsp;world]
015&nbsp;&nbsp;&nbsp;&nbsp;[enquirer&nbsp;respondent&nbsp;world]
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
015&nbsp;&nbsp;&nbsp;&nbsp;enquirer)
016&nbsp;&nbsp;&nbsp;&nbsp;enquirer)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
016&nbsp;&nbsp;
017&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
017&nbsp;&nbsp;(defn&nbsp;gather-news
018&nbsp;&nbsp;(defn&nbsp;gather-news
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;&nbsp;&nbsp;([world]
019&nbsp;&nbsp;&nbsp;&nbsp;([world]
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
019&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deep-merge
021&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deep-merge
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
021&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;world
022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;world
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(gather-news&nbsp;world&nbsp;%)
024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(gather-news&nbsp;world&nbsp;%)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keys&nbsp;(:gossips&nbsp;world)))))
025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keys&nbsp;(:gossips&nbsp;world)))))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;&nbsp;&nbsp;([world&nbsp;gossip]
026&nbsp;&nbsp;&nbsp;&nbsp;([world&nbsp;gossip]
</span><br/>
<span class="not-covered" title="0 out of 7 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[g&nbsp;(cond&nbsp;(keyword?&nbsp;gossip)
027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[g&nbsp;(cond&nbsp;(keyword?&nbsp;gossip)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:gossips&nbsp;gossip)
028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:gossips&nbsp;gossip)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;gossip)
029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;gossip)
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gossip)]
030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gossip)]
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:gossips
031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:gossips
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{(:id&nbsp;g)
032&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{(:id&nbsp;g)
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
033&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deep-merge
034&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deep-merge
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
034&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{}
035&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{}
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
036&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
036&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(dialogue&nbsp;g&nbsp;%&nbsp;world)
037&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(dialogue&nbsp;g&nbsp;%&nbsp;world)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(remove
038&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(remove
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
038&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(&nbsp;=&nbsp;g&nbsp;%)
039&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(&nbsp;=&nbsp;g&nbsp;%)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter
040&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter
</span><br/>
<span class="not-covered" title="0 out of 8 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(=&nbsp;(:location&nbsp;%)&nbsp;(:location&nbsp;g))
041&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(=&nbsp;(:location&nbsp;%)&nbsp;(:location&nbsp;g))
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(vals&nbsp;(:gossips&nbsp;world))))))}})))
042&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(vals&nbsp;(:gossips&nbsp;world))))))}})))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
042&nbsp;&nbsp;
043&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
043&nbsp;&nbsp;(defn&nbsp;move-gossip
044&nbsp;&nbsp;(defn&nbsp;move-gossip
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;world&nbsp;like&nbsp;this&nbsp;`world`&nbsp;but&nbsp;with&nbsp;this&nbsp;`gossip`&nbsp;moved&nbsp;to&nbsp;this
045&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;world&nbsp;like&nbsp;this&nbsp;`world`&nbsp;but&nbsp;with&nbsp;this&nbsp;`gossip`&nbsp;moved&nbsp;to&nbsp;this
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;`new-location`.&nbsp;Many&nbsp;gossips&nbsp;are&nbsp;essentially&nbsp;shadow-records&nbsp;of&nbsp;agents&nbsp;of
046&nbsp;&nbsp;&nbsp;&nbsp;`new-location`.&nbsp;Many&nbsp;gossips&nbsp;are&nbsp;essentially&nbsp;shadow-records&nbsp;of&nbsp;agents&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;other&nbsp;types,&nbsp;and&nbsp;the&nbsp;movement&nbsp;if&nbsp;the&nbsp;gossip&nbsp;should&nbsp;be&nbsp;controlled&nbsp;by&nbsp;the
047&nbsp;&nbsp;&nbsp;&nbsp;other&nbsp;types,&nbsp;and&nbsp;the&nbsp;movement&nbsp;of&nbsp;the&nbsp;gossip&nbsp;should&nbsp;be&nbsp;controlled&nbsp;by&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
047&nbsp;&nbsp;&nbsp;&nbsp;run&nbsp;function&nbsp;of&nbsp;the&nbsp;type&nbsp;of&nbsp;the&nbsp;record&nbsp;they&nbsp;shadow.&nbsp;The&nbsp;[[#run]]&nbsp;function
048&nbsp;&nbsp;&nbsp;&nbsp;run&nbsp;function&nbsp;of&nbsp;the&nbsp;type&nbsp;of&nbsp;the&nbsp;record&nbsp;they&nbsp;shadow.&nbsp;The&nbsp;[[#run]]&nbsp;function
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
048&nbsp;&nbsp;&nbsp;&nbsp;below&nbsp;does&nbsp;NOT&nbsp;call&nbsp;this&nbsp;function.&quot;
049&nbsp;&nbsp;&nbsp;&nbsp;below&nbsp;does&nbsp;NOT&nbsp;call&nbsp;this&nbsp;function.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;world&nbsp;new-location]
050&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;world&nbsp;new-location]
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[id&nbsp;(cond
051&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[id&nbsp;(cond
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;gossip)
052&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;gossip)
</span><br/>
<span class="not-covered" title="0 out of 7 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:gossips&nbsp;gossip&nbsp;:id)
053&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:gossips&nbsp;gossip&nbsp;:id)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;gossip)
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;gossip)
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gossip)]
055&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gossip)]
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;(deep-merge
056&nbsp;&nbsp;&nbsp;&nbsp;(deep-merge
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;world
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;world
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:gossips
058&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:gossips
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{id
059&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{id
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:location&nbsp;new-location}}})))
060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:location&nbsp;new-location}}})))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
060&nbsp;&nbsp;
061&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
061&nbsp;&nbsp;(defn&nbsp;run
062&nbsp;&nbsp;(defn&nbsp;run
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;world&nbsp;like&nbsp;this&nbsp;`world`,&nbsp;with&nbsp;news&nbsp;items&nbsp;exchanged&nbsp;between&nbsp;gossip
063&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;world&nbsp;like&nbsp;this&nbsp;`world`,&nbsp;with&nbsp;news&nbsp;items&nbsp;exchanged&nbsp;between&nbsp;gossip
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;agents.&quot;
064&nbsp;&nbsp;&nbsp;&nbsp;agents.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;[world]
065&nbsp;&nbsp;&nbsp;&nbsp;[world]
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;(gather-news&nbsp;world))
066&nbsp;&nbsp;&nbsp;&nbsp;(gather-news&nbsp;world))
</span><br/>
</body>
</html>

View file

@ -0,0 +1,776 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../../coverage.css"/> <title> the_great_game/gossip/news_items.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;the-great-game.gossip.news-items
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&quot;Categories&nbsp;of&nbsp;news&nbsp;events&nbsp;interesting&nbsp;to&nbsp;gossip&nbsp;agents&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[the-great-game.world.location&nbsp;:refer&nbsp;[distance-between]]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[the-great-game.time&nbsp;:refer&nbsp;[game-time]]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
005&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;;;&nbsp;The&nbsp;ideas&nbsp;here&nbsp;are&nbsp;based&nbsp;on&nbsp;the&nbsp;essay&nbsp;&#x27;The&nbsp;spread&nbsp;of&nbsp;knowledge&nbsp;in&nbsp;a&nbsp;large
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;;;&nbsp;game&nbsp;world&#x27;,&nbsp;q.v.;&nbsp;they&#x27;ve&nbsp;advanced&nbsp;a&nbsp;little&nbsp;beyond&nbsp;that&nbsp;and&nbsp;will&nbsp;doubtless
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;;;&nbsp;advance&nbsp;further&nbsp;in&nbsp;the&nbsp;course&nbsp;of&nbsp;writing&nbsp;and&nbsp;debugging&nbsp;this&nbsp;namespace.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
009&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;;;&nbsp;A&nbsp;news&nbsp;item&nbsp;is&nbsp;a&nbsp;map&nbsp;with&nbsp;the&nbsp;keys:
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;;;&nbsp;*&nbsp;`date`&nbsp;-&nbsp;the&nbsp;date&nbsp;on&nbsp;which&nbsp;the&nbsp;reported&nbsp;event&nbsp;happened;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;;;&nbsp;*&nbsp;`nth-hand`&nbsp;-&nbsp;the&nbsp;number&nbsp;of&nbsp;agents&nbsp;the&nbsp;news&nbsp;item&nbsp;has&nbsp;passed&nbsp;through;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;;;&nbsp;*&nbsp;`verb`&nbsp;-&nbsp;what&nbsp;it&nbsp;is&nbsp;that&nbsp;happened&nbsp;(key&nbsp;into&nbsp;`news-topics`);
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;;;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;;;&nbsp;plus&nbsp;other&nbsp;keys&nbsp;taken&nbsp;from&nbsp;the&nbsp;`keys`&nbsp;value&nbsp;associated&nbsp;with&nbsp;the&nbsp;verb&nbsp;in
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;;;&nbsp;`news-topics`
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
018&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
019&nbsp;&nbsp;(def&nbsp;news-topics
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;&nbsp;&nbsp;&quot;Topics&nbsp;of&nbsp;interest&nbsp;to&nbsp;gossip&nbsp;agents.&nbsp;Topics&nbsp;are&nbsp;keyed&nbsp;in&nbsp;this&nbsp;map&nbsp;by
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;&nbsp;&nbsp;their&nbsp;`verbs`.&nbsp;The&nbsp;`keys`&nbsp;associated&nbsp;with&nbsp;each&nbsp;topic&nbsp;are&nbsp;the&nbsp;extra&nbsp;pieces
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;&nbsp;&nbsp;of&nbsp;information&nbsp;required&nbsp;to&nbsp;give&nbsp;context&nbsp;to&nbsp;a&nbsp;gossip&nbsp;item.&nbsp;Generally:
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
023&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
024&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`actor`&nbsp;is&nbsp;the&nbsp;id&nbsp;of&nbsp;the&nbsp;character&nbsp;who&nbsp;it&nbsp;is&nbsp;reported&nbsp;performed&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
025&nbsp;&nbsp;&nbsp;&nbsp;action;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`other`&nbsp;is&nbsp;the&nbsp;id&nbsp;of&nbsp;the&nbsp;character&nbsp;on&nbsp;whom&nbsp;it&nbsp;is&nbsp;reported&nbsp;the&nbsp;action
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
027&nbsp;&nbsp;&nbsp;&nbsp;was&nbsp;performed;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`location`&nbsp;is&nbsp;the&nbsp;place&nbsp;at&nbsp;which&nbsp;the&nbsp;action&nbsp;was&nbsp;performed;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
029&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`object`&nbsp;is&nbsp;an&nbsp;object&nbsp;(or&nbsp;possibly&nbsp;list&nbsp;of&nbsp;objects?)&nbsp;relevant&nbsp;to&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;action;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;`price`&nbsp;is&nbsp;special&nbsp;to&nbsp;buy&#x2F;sell,&nbsp;but&nbsp;of&nbsp;significant&nbsp;interest&nbsp;to&nbsp;merchants.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
032&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;####&nbsp;Notes:
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
034&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;#####&nbsp;Characters:
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
036&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;*TODO*&nbsp;but&nbsp;note&nbsp;that&nbsp;at&nbsp;most&nbsp;all&nbsp;the&nbsp;receiver&nbsp;can&nbsp;learn&nbsp;about&nbsp;a&nbsp;character
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
038&nbsp;&nbsp;&nbsp;&nbsp;from&nbsp;a&nbsp;news&nbsp;item&nbsp;is&nbsp;what&nbsp;the&nbsp;giver&nbsp;knows&nbsp;about&nbsp;that&nbsp;character,&nbsp;degraded&nbsp;by
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;what&nbsp;the&nbsp;receiver&nbsp;finds&nbsp;interesting&nbsp;about&nbsp;them.&nbsp;If&nbsp;we&nbsp;just&nbsp;pass&nbsp;the&nbsp;id&nbsp;here,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;then&nbsp;either&nbsp;the&nbsp;receiver&nbsp;knows&nbsp;everything&nbsp;in&nbsp;the&nbsp;database&nbsp;about&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;character,&nbsp;or&nbsp;else&nbsp;the&nbsp;receiver&nbsp;knows&nbsp;nothing&nbsp;at&nbsp;all&nbsp;about&nbsp;the&nbsp;character.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
042&nbsp;&nbsp;&nbsp;&nbsp;Neither&nbsp;is&nbsp;desirable.&nbsp;Further&nbsp;thought&nbsp;needed.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
043&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;By&nbsp;implication,&nbsp;the&nbsp;character&nbsp;values&nbsp;passed&nbsp;should&nbsp;include&nbsp;*all*&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;information&nbsp;the&nbsp;giver&nbsp;knows&nbsp;about&nbsp;the&nbsp;character;&nbsp;that&nbsp;can&nbsp;then&nbsp;be&nbsp;degraded
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;as&nbsp;the&nbsp;receiver&nbsp;stores&nbsp;only&nbsp;that&nbsp;segment&nbsp;which&nbsp;the&nbsp;receiver&nbsp;finds
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
047&nbsp;&nbsp;&nbsp;&nbsp;interesting.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
048&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;#####&nbsp;Locations:
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
050&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;&#x27;location&#x27;&nbsp;value&nbsp;is&nbsp;a&nbsp;list&nbsp;comprising&nbsp;at&nbsp;most&nbsp;the&nbsp;x&#x2F;y&nbsp;coordinate&nbsp;location
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;and&nbsp;the&nbsp;ids&nbsp;of&nbsp;the&nbsp;settlement&nbsp;and&nbsp;region&nbsp;(possibly&nbsp;hierarchically)&nbsp;that&nbsp;contain
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;location.&nbsp;If&nbsp;the&nbsp;x&#x2F;y&nbsp;is&nbsp;not&nbsp;local&nbsp;to&nbsp;the&nbsp;home&nbsp;of&nbsp;the&nbsp;receiving&nbsp;agent,&nbsp;they
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;won&#x27;t&nbsp;remember&nbsp;it&nbsp;and&nbsp;won&#x27;t&nbsp;pass&nbsp;it&nbsp;on;&nbsp;if&nbsp;any&nbsp;of&nbsp;the&nbsp;ids&nbsp;are&nbsp;not&nbsp;interesting
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;So&nbsp;location&nbsp;information&nbsp;will&nbsp;degrade&nbsp;progressively&nbsp;as&nbsp;the&nbsp;item&nbsp;is&nbsp;passed&nbsp;along.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
056&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;It&nbsp;is&nbsp;assumed&nbsp;that&nbsp;the&nbsp;`:home`&nbsp;of&nbsp;a&nbsp;character&nbsp;is&nbsp;a&nbsp;location&nbsp;in&nbsp;this&nbsp;sense.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
058&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;#####&nbsp;Inferences:
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
060&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;If&nbsp;an&nbsp;agent&nbsp;learns&nbsp;that&nbsp;Adam&nbsp;has&nbsp;married&nbsp;Betty,&nbsp;they&nbsp;can&nbsp;infer&nbsp;that&nbsp;Betty&nbsp;has
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;married&nbsp;Adam;&nbsp;if&nbsp;they&nbsp;learn&nbsp;that&nbsp;Charles&nbsp;killed&nbsp;Dorothy,&nbsp;that&nbsp;Dorothy&nbsp;has&nbsp;died.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;I&#x27;m&nbsp;not&nbsp;convinced&nbsp;that&nbsp;my&nbsp;representation&nbsp;of&nbsp;inferences&nbsp;here&nbsp;is&nbsp;ideal.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&quot;
</span><br/>
<span class="covered" title="13 out of 13 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;;;&nbsp;A&nbsp;significant&nbsp;attack&nbsp;is&nbsp;interesting&nbsp;whether&nbsp;or&nbsp;not&nbsp;it&nbsp;leads&nbsp;to&nbsp;deaths
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:attack&nbsp;{:verb&nbsp;:attack&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:location]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Deaths&nbsp;of&nbsp;characters&nbsp;may&nbsp;be&nbsp;interesting
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
068&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:die&nbsp;{:verb&nbsp;:die&nbsp;:keys&nbsp;[:actor&nbsp;:location]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
069&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Deliberate&nbsp;killings&nbsp;are&nbsp;interesting.
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
070&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:kill&nbsp;{:verb&nbsp;:kill&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:location]
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
071&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:inferences&nbsp;[{:verb&nbsp;:die&nbsp;:actor&nbsp;:other&nbsp;:other&nbsp;:nil}]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
072&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Marriages&nbsp;may&nbsp;be&nbsp;interesting
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
073&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:marry&nbsp;{:verb&nbsp;:marry&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:location]
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
074&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:inferences&nbsp;[{:verb&nbsp;:marry&nbsp;:actor&nbsp;:other&nbsp;:other&nbsp;:actor}]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
075&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;The&nbsp;end&nbsp;of&nbsp;ongoing&nbsp;open&nbsp;conflict&nbsp;between&nbsp;to&nbsp;characters&nbsp;may&nbsp;be&nbsp;interesting
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
076&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:peace&nbsp;{:verb&nbsp;:peace&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:location]
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
077&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:inferences&nbsp;[{:verb&nbsp;:peace&nbsp;:actor&nbsp;:other&nbsp;:other&nbsp;:actor}]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
078&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Things&nbsp;related&nbsp;to&nbsp;the&nbsp;plot&nbsp;are&nbsp;interesting,&nbsp;but&nbsp;will&nbsp;require&nbsp;special
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
079&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;handling.&nbsp;Extra&nbsp;keys&nbsp;may&nbsp;be&nbsp;required&nbsp;by&nbsp;particular&nbsp;plot&nbsp;events.
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
080&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:plot&nbsp;{:verb&nbsp;:plot&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:object&nbsp;:location]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
081&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Rapes&nbsp;are&nbsp;interesting.
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
082&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:rape&nbsp;{:verb&nbsp;:rape&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:location]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
083&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Should&nbsp;you&nbsp;also&nbsp;infer&nbsp;from&nbsp;rape&nbsp;that&nbsp;actor&nbsp;is&nbsp;male&nbsp;and&nbsp;adult?
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
084&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:inferences&nbsp;[{:verb&nbsp;:attack}
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
085&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:verb&nbsp;:sex}
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
086&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:verb&nbsp;:sex&nbsp;:actor&nbsp;:other&nbsp;:other&nbsp;:actor}]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
087&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Merchants,&nbsp;especially,&nbsp;are&nbsp;interested&nbsp;in&nbsp;prices&nbsp;in&nbsp;other&nbsp;markets
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
088&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:sell&nbsp;{:verb&nbsp;:sell&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:object&nbsp;:location&nbsp;:price]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
089&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Sex&nbsp;can&nbsp;juicy&nbsp;gossip,&nbsp;although&nbsp;not&nbsp;normally&nbsp;if&nbsp;the&nbsp;participants&nbsp;are&nbsp;in&nbsp;an
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
090&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;established&nbsp;sexual&nbsp;relationship.
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
091&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:sex&nbsp;{:verb&nbsp;:sex&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:location]
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
092&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:inferences&nbsp;[{:verb&nbsp;:sex&nbsp;:actor&nbsp;:other&nbsp;:other&nbsp;:actor}]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
093&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;Thefts&nbsp;are&nbsp;interesting
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
094&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:steal&nbsp;{:verb&nbsp;:steal&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:object&nbsp;:location]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
095&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;The&nbsp;succession&nbsp;of&nbsp;rulers&nbsp;is&nbsp;interesting;&nbsp;of&nbsp;respected&nbsp;craftsmen,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
096&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;potentially&nbsp;also&nbsp;interesting.
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
097&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:succession&nbsp;{:verb&nbsp;:succession&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:location&nbsp;:rank]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
098&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;The&nbsp;start&nbsp;of&nbsp;ongoing&nbsp;open&nbsp;conflict&nbsp;between&nbsp;to&nbsp;characters&nbsp;may&nbsp;be&nbsp;interesting
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
099&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:war&nbsp;{:verb&nbsp;:war&nbsp;:keys&nbsp;[:actor&nbsp;:other&nbsp;:location]
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:inferences&nbsp;[{:verb&nbsp;:war&nbsp;:actor&nbsp;:other&nbsp;:other&nbsp;:actor}]}
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;})
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
102&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
103&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
104&nbsp;&nbsp;(defn&nbsp;interest-in-character
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
105&nbsp;&nbsp;&nbsp;&nbsp;&quot;Integer&nbsp;representation&nbsp;of&nbsp;how&nbsp;interesting&nbsp;this&nbsp;`character`&nbsp;is&nbsp;to&nbsp;this
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
106&nbsp;&nbsp;&nbsp;&nbsp;`gossip`.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
107&nbsp;&nbsp;&nbsp;&nbsp;*TODO:*&nbsp;this&nbsp;assumes&nbsp;that&nbsp;characters&nbsp;are&nbsp;passed&nbsp;as&nbsp;keywords,&nbsp;but,&nbsp;as
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
108&nbsp;&nbsp;&nbsp;&nbsp;documented&nbsp;above,&nbsp;they&nbsp;probably&nbsp;have&nbsp;to&nbsp;be&nbsp;maps,&nbsp;to&nbsp;allow&nbsp;for&nbsp;degradation.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
109&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;character]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
110&nbsp;&nbsp;&nbsp;&nbsp;(count
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
111&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(concat
</span><br/>
<span class="partial" title="6 out of 12 forms covered">
112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter&nbsp;#(=&nbsp;(:actor&nbsp;%&nbsp;character))&nbsp;(:knowledge&nbsp;gossip))
</span><br/>
<span class="partial" title="6 out of 12 forms covered">
113&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter&nbsp;#(=&nbsp;(:other&nbsp;%&nbsp;character))&nbsp;(:knowledge&nbsp;gossip)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
114&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
115&nbsp;&nbsp;(defn&nbsp;interesting-character?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
116&nbsp;&nbsp;&nbsp;&nbsp;&quot;Boolean&nbsp;representation&nbsp;of&nbsp;whether&nbsp;this&nbsp;`character`&nbsp;is&nbsp;interesting&nbsp;to&nbsp;this
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
117&nbsp;&nbsp;&nbsp;&nbsp;`gossip`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
118&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;character]
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
119&nbsp;&nbsp;&nbsp;&nbsp;(&gt;&nbsp;(interest-in-character&nbsp;gossip&nbsp;character)&nbsp;0))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
120&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
121&nbsp;&nbsp;(defn&nbsp;interest-in-location
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
122&nbsp;&nbsp;&nbsp;&nbsp;&quot;Integer&nbsp;representation&nbsp;of&nbsp;how&nbsp;interesting&nbsp;this&nbsp;`location`&nbsp;is&nbsp;to&nbsp;this
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
123&nbsp;&nbsp;&nbsp;&nbsp;`gossip`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
124&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;location]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
125&nbsp;&nbsp;&nbsp;&nbsp;(cond
</span><br/>
<span class="partial" title="20 out of 21 forms covered">
126&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(and&nbsp;(map?&nbsp;location)&nbsp;(number?&nbsp;(:x&nbsp;location))&nbsp;(number?&nbsp;(:y&nbsp;location)))
</span><br/>
<span class="partial" title="8 out of 9 forms covered">
127&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if-let&nbsp;[home&nbsp;(:home&nbsp;gossip)]
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
128&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[d&nbsp;(distance-between&nbsp;location&nbsp;home)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
129&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i&nbsp;(&#x2F;&nbsp;10000&nbsp;d)&nbsp;;;&nbsp;10000&nbsp;at&nbsp;metre&nbsp;scale&nbsp;is&nbsp;10km;&nbsp;interest&nbsp;should
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
130&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;fall&nbsp;off&nbsp;with&nbsp;distance&nbsp;from&nbsp;home,&nbsp;but&nbsp;possibly&nbsp;on&nbsp;a&nbsp;log&nbsp;scale
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
131&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
132&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(&gt;&nbsp;i&nbsp;1)&nbsp;i&nbsp;0))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
133&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
134&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(coll?&nbsp;location)
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
135&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
136&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
137&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
138&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(interest-in-location&nbsp;gossip&nbsp;%)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
139&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;location))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
140&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:else
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
141&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(count
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
142&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
143&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(some&nbsp;(fn&nbsp;[x]&nbsp;(=&nbsp;x&nbsp;location))&nbsp;(:location&nbsp;%))
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
144&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cons&nbsp;{:location&nbsp;(:home&nbsp;gossip)}&nbsp;(:knowledge&nbsp;gossip))))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
145&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
146&nbsp;&nbsp;;;&nbsp;(interest-in-location&nbsp;{:home&nbsp;[{0,&nbsp;0}&nbsp;:test-home]&nbsp;:knowledge&nbsp;[]}&nbsp;[:test-home])
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
147&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
148&nbsp;&nbsp;(defn&nbsp;interesting-location?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
149&nbsp;&nbsp;&nbsp;&nbsp;&quot;True&nbsp;if&nbsp;the&nbsp;location&nbsp;of&nbsp;this&nbsp;news&nbsp;`item`&nbsp;is&nbsp;interesting&nbsp;to&nbsp;this&nbsp;`gossip`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
150&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;item]
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
151&nbsp;&nbsp;&nbsp;&nbsp;(&gt;&nbsp;(interest-in-location&nbsp;gossip&nbsp;(:location&nbsp;item))&nbsp;0))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
152&nbsp;&nbsp;
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
153&nbsp;&nbsp;(defn&nbsp;interesting-object?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
154&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;object]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
155&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;Not&nbsp;yet&nbsp;(really)&nbsp;implemented
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
156&nbsp;&nbsp;&nbsp;&nbsp;true)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
157&nbsp;&nbsp;
</span><br/>
<span class="partial" title="1 out of 2 forms covered">
158&nbsp;&nbsp;(defn&nbsp;interesting-topic?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
159&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;topic]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
160&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;Not&nbsp;yet&nbsp;(really)&nbsp;implemented
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
161&nbsp;&nbsp;&nbsp;&nbsp;true)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
162&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
163&nbsp;&nbsp;(defn&nbsp;interesting-item?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
164&nbsp;&nbsp;&nbsp;&nbsp;&quot;True&nbsp;if&nbsp;anything&nbsp;about&nbsp;this&nbsp;news&nbsp;`item`&nbsp;is&nbsp;interesting&nbsp;to&nbsp;this&nbsp;`gossip`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
165&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;item]
</span><br/>
<span class="partial" title="13 out of 17 forms covered">
166&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(or
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
167&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(interesting-character?&nbsp;gossip&nbsp;(:actor&nbsp;item))
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
168&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(interesting-character?&nbsp;gossip&nbsp;(:other&nbsp;item))
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
169&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(interesting-location?&nbsp;gossip&nbsp;(:location&nbsp;item))
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
170&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(interesting-object?&nbsp;gossip&nbsp;(:object&nbsp;item))
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
171&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(interesting-topic?&nbsp;gossip&nbsp;(:verb&nbsp;item))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
172&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
173&nbsp;&nbsp;(defn&nbsp;infer
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
174&nbsp;&nbsp;&nbsp;&nbsp;&quot;Infer&nbsp;a&nbsp;new&nbsp;knowledge&nbsp;item&nbsp;from&nbsp;this&nbsp;`item`,&nbsp;following&nbsp;this&nbsp;`rule`&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
175&nbsp;&nbsp;&nbsp;&nbsp;[item&nbsp;rule]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
176&nbsp;&nbsp;&nbsp;&nbsp;(reduce&nbsp;merge
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
177&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
178&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cons
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
179&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:verb&nbsp;(:verb&nbsp;rule)}
</span><br/>
<span class="covered" title="13 out of 13 forms covered">
180&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map&nbsp;(fn&nbsp;[k]&nbsp;{k&nbsp;(apply&nbsp;(k&nbsp;rule)&nbsp;(list&nbsp;item))})
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
181&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(remove
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
182&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(=&nbsp;%&nbsp;:verb)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
183&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keys&nbsp;rule))))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
184&nbsp;&nbsp;
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
185&nbsp;&nbsp;(declare&nbsp;learn-news-item)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
186&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
187&nbsp;&nbsp;(defn&nbsp;make-all-inferences
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
188&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;list&nbsp;of&nbsp;knowledge&nbsp;entries&nbsp;that&nbsp;can&nbsp;be&nbsp;inferred&nbsp;from&nbsp;this&nbsp;news
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
189&nbsp;&nbsp;&nbsp;&nbsp;`item`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
190&nbsp;&nbsp;&nbsp;&nbsp;[item]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
191&nbsp;&nbsp;&nbsp;&nbsp;(set
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
192&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
193&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;concat
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
194&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
195&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(:knowledge&nbsp;(learn-news-item&nbsp;{}&nbsp;(infer&nbsp;item&nbsp;%)&nbsp;false))
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
196&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:inferences&nbsp;(news-topics&nbsp;(:verb&nbsp;item)))))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
197&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
198&nbsp;&nbsp;(defn&nbsp;degrade-character
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
199&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;character&nbsp;specification&nbsp;like&nbsp;this&nbsp;`character`,&nbsp;but&nbsp;comprising
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
200&nbsp;&nbsp;&nbsp;&nbsp;only&nbsp;those&nbsp;properties&nbsp;this&nbsp;`gossip`&nbsp;is&nbsp;interested&nbsp;in.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
201&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;character]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
202&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;Not&nbsp;yet&nbsp;(really)&nbsp;implemented
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
203&nbsp;&nbsp;&nbsp;&nbsp;character)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
204&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
205&nbsp;&nbsp;(defn&nbsp;degrade-location
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
206&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;location&nbsp;specification&nbsp;like&nbsp;this&nbsp;`location`,&nbsp;but&nbsp;comprising
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
207&nbsp;&nbsp;&nbsp;&nbsp;only&nbsp;those&nbsp;elements&nbsp;this&nbsp;`gossip`&nbsp;is&nbsp;interested&nbsp;in.&nbsp;If&nbsp;none,&nbsp;return
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
208&nbsp;&nbsp;&nbsp;&nbsp;`nil`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
209&nbsp;&nbsp;&nbsp;&nbsp;[gossip&nbsp;location]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
210&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[l&nbsp;(if
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
211&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(coll?&nbsp;location)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
212&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter
</span><br/>
<span class="partial" title="5 out of 7 forms covered">
213&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(when&nbsp;(interesting-location?&nbsp;gossip&nbsp;%)&nbsp;%)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
214&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;location))]
</span><br/>
<span class="partial" title="5 out of 7 forms covered">
215&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(when-not&nbsp;(empty?&nbsp;l)&nbsp;l)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
216&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
217&nbsp;&nbsp;(defn&nbsp;learn-news-item
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
218&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;gossip&nbsp;like&nbsp;this&nbsp;`gossip`,&nbsp;which&nbsp;has&nbsp;learned&nbsp;this&nbsp;news&nbsp;`item`&nbsp;if
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
219&nbsp;&nbsp;&nbsp;&nbsp;it&nbsp;is&nbsp;of&nbsp;interest&nbsp;to&nbsp;them.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
220&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;Not&nbsp;yet&nbsp;implemented
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
221&nbsp;&nbsp;&nbsp;&nbsp;([gossip&nbsp;item]
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
222&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(learn-news-item&nbsp;gossip&nbsp;item&nbsp;true))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
223&nbsp;&nbsp;&nbsp;&nbsp;([gossip&nbsp;item&nbsp;follow-inferences?]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
224&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
225&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(interesting-item?&nbsp;gossip&nbsp;item)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
226&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
227&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[g&nbsp;(assoc
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
228&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gossip
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
229&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:knowledge
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
230&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cons
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
231&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assoc
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
232&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
233&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:nth-hand&nbsp;(if
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
234&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(number?&nbsp;(:nth-hand&nbsp;item))
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
235&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(inc&nbsp;(:nth-hand&nbsp;item))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
236&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
237&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:time-stamp&nbsp;(if
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
238&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(number?&nbsp;(:time-stamp&nbsp;item))
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
239&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:time-stamp&nbsp;item)
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
240&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(game-time))
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
241&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:location&nbsp;(degrade-location&nbsp;gossip&nbsp;(:location&nbsp;item))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
242&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;ought&nbsp;to&nbsp;maybe-degrade&nbsp;characters&nbsp;we&#x27;re&nbsp;not&nbsp;yet&nbsp;interested&nbsp;in
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
243&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
244&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;TODO:&nbsp;ought&nbsp;not&nbsp;to&nbsp;add&nbsp;knowledge&nbsp;items&nbsp;we&nbsp;already&nbsp;have,&nbsp;except
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
245&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;to&nbsp;replace&nbsp;if&nbsp;new&nbsp;item&nbsp;is&nbsp;of&nbsp;increased&nbsp;specificity
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
246&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:knowledge&nbsp;gossip)))]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
247&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;follow-inferences?
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
248&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(assoc
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
249&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
250&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:knowledge
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
251&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(concat&nbsp;(:knowledge&nbsp;g)&nbsp;(make-all-inferences&nbsp;item)))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
252&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;g))
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
253&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gossip)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
254&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
255&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
256&nbsp;&nbsp;
</span><br/>
</body>
</html>

View file

@ -79,22 +79,22 @@
<span class="covered" title="3 out of 3 forms covered">
025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id&nbsp;(:id&nbsp;c)
</span><br/>
<span class="partial" title="9 out of 10 forms covered">
<span class="covered" title="10 out of 10 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;(or&nbsp;(-&gt;&nbsp;c&nbsp;:prices&nbsp;commodity)&nbsp;0)
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d&nbsp;(or&nbsp;(-&gt;&nbsp;c&nbsp;:demands&nbsp;commodity)&nbsp;0)
</span><br/>
<span class="partial" title="9 out of 10 forms covered">
<span class="covered" title="10 out of 10 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;st&nbsp;(or&nbsp;(-&gt;&nbsp;c&nbsp;:stock&nbsp;commodity)&nbsp;0)
</span><br/>
<span class="partial" title="9 out of 10 forms covered">
<span class="covered" title="10 out of 10 forms covered">
029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;su&nbsp;(or&nbsp;(-&gt;&nbsp;c&nbsp;:supplies&nbsp;commodity)&nbsp;0)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;decrement&nbsp;(min&nbsp;st&nbsp;d)
</span><br/>
<span class="partial" title="3 out of 6 forms covered">
<span class="partial" title="5 out of 6 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;increment&nbsp;(cond
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
@ -190,46 +190,46 @@
<span class="not-tracked" title="0 out of 0 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;([world]
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
<span class="covered" title="2 out of 2 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
<span class="covered" title="1 out of 1 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deep-merge
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
<span class="covered" title="1 out of 1 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;world
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
<span class="covered" title="3 out of 3 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
<span class="covered" title="4 out of 4 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(update-markets&nbsp;world&nbsp;%)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
<span class="covered" title="5 out of 5 forms covered">
068&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keys&nbsp;(:cities&nbsp;world)))))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
069&nbsp;&nbsp;&nbsp;&nbsp;([world&nbsp;city]
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
<span class="covered" title="2 out of 2 forms covered">
070&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
<span class="covered" title="1 out of 1 forms covered">
071&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;deep-merge
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
<span class="covered" title="1 out of 1 forms covered">
072&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{}
</span><br/>
<span class="not-covered" title="0 out of 8 forms covered">
<span class="covered" title="8 out of 8 forms covered">
073&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map&nbsp;#(update-markets&nbsp;world&nbsp;city&nbsp;%)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
<span class="covered" title="5 out of 5 forms covered">
074&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keys&nbsp;(:commodities&nbsp;world)))))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
075&nbsp;&nbsp;&nbsp;&nbsp;([world&nbsp;city&nbsp;commodity]
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
<span class="covered" title="5 out of 5 forms covered">
076&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(adjust-quantity-and-price&nbsp;world&nbsp;city&nbsp;commodity)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
@ -250,7 +250,7 @@
<span class="not-tracked" title="0 out of 0 forms covered">
082&nbsp;&nbsp;&nbsp;&nbsp;[world]
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
<span class="covered" title="3 out of 3 forms covered">
083&nbsp;&nbsp;&nbsp;&nbsp;(update-markets&nbsp;world))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">

View file

@ -64,13 +64,13 @@
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;&nbsp;&nbsp;[merchant&nbsp;world]
</span><br/>
<span class="partial" title="3 out of 4 forms covered">
<span class="covered" title="4 out of 4 forms covered">
021&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[m&nbsp;(cond
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;merchant)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
<span class="covered" title="5 out of 5 forms covered">
023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:merchants&nbsp;merchant)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
@ -79,8 +79,8 @@
<span class="covered" title="1 out of 1 forms covered">
025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merchant)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cargo&nbsp;(:stock&nbsp;m)]
<span class="covered" title="8 out of 8 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cargo&nbsp;(or&nbsp;(:stock&nbsp;m)&nbsp;{})]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
@ -94,7 +94,7 @@
<span class="covered" title="3 out of 3 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="not-covered" title="0 out of 12 forms covered">
<span class="covered" title="12 out of 12 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(*&nbsp;(cargo&nbsp;%)&nbsp;(-&gt;&nbsp;world&nbsp;:commodities&nbsp;%&nbsp;:weight))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
@ -124,7 +124,7 @@
<span class="covered" title="3 out of 3 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;merchant)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
<span class="covered" title="5 out of 5 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:merchants&nbsp;merchant)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
@ -133,152 +133,194 @@
<span class="covered" title="1 out of 1 forms covered">
043&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merchant)]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(quot
<span class="covered" title="3 out of 3 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(max
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&nbsp;(:capacity&nbsp;m)&nbsp;(burden&nbsp;m&nbsp;world))
<span class="not-tracked" title="0 out of 0 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(quot
</span><br/>
<span class="partial" title="13 out of 14 forms covered">
047&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&nbsp;(or&nbsp;(:capacity&nbsp;m)&nbsp;0)&nbsp;(burden&nbsp;m&nbsp;world))
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:commodities&nbsp;commodity&nbsp;:weight))))
048&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:commodities&nbsp;commodity&nbsp;:weight)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
047&nbsp;&nbsp;
049&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
048&nbsp;&nbsp;(defn&nbsp;can-afford
050&nbsp;&nbsp;(defn&nbsp;can-afford
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;the&nbsp;number&nbsp;of&nbsp;units&nbsp;of&nbsp;this&nbsp;`commodity`&nbsp;which&nbsp;this&nbsp;`merchant`
051&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;the&nbsp;number&nbsp;of&nbsp;units&nbsp;of&nbsp;this&nbsp;`commodity`&nbsp;which&nbsp;this&nbsp;`merchant`
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;can&nbsp;afford&nbsp;to&nbsp;buy&nbsp;in&nbsp;this&nbsp;`world`.&quot;
052&nbsp;&nbsp;&nbsp;&nbsp;can&nbsp;afford&nbsp;to&nbsp;buy&nbsp;in&nbsp;this&nbsp;`world`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;[merchant&nbsp;world&nbsp;commodity]
053&nbsp;&nbsp;&nbsp;&nbsp;[merchant&nbsp;world&nbsp;commodity]
</span><br/>
<span class="partial" title="3 out of 4 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[m&nbsp;(cond
054&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[m&nbsp;(cond
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;merchant)
055&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;merchant)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:merchants&nbsp;merchant)
<span class="covered" title="5 out of 5 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:merchants&nbsp;merchant)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;merchant)
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;merchant)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merchant)
058&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merchant)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l&nbsp;(:location&nbsp;m)]
059&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l&nbsp;(:location&nbsp;m)]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cond
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(nil?&nbsp;m)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw&nbsp;(Exception.&nbsp;&quot;No&nbsp;merchant?&quot;))
</span><br/>
<span class="covered" title="14 out of 14 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(or&nbsp;(nil?&nbsp;l)&nbsp;(nil?&nbsp;(-&gt;&nbsp;world&nbsp;:cities&nbsp;l)))
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw&nbsp;(Exception.&nbsp;(str&nbsp;&quot;No&nbsp;known&nbsp;location&nbsp;for&nbsp;merchant&nbsp;&quot;&nbsp;m)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:else
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(quot
066&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(quot
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:cash&nbsp;m)
067&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:cash&nbsp;m)
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:cities&nbsp;l&nbsp;:prices&nbsp;commodity))))
068&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:cities&nbsp;l&nbsp;:prices&nbsp;commodity)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
061&nbsp;&nbsp;
069&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
062&nbsp;&nbsp;(defn&nbsp;add-stock
070&nbsp;&nbsp;(defn&nbsp;add-stock
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&quot;Where&nbsp;`a`&nbsp;and&nbsp;`b`&nbsp;are&nbsp;both&nbsp;maps&nbsp;all&nbsp;of&nbsp;whose&nbsp;values&nbsp;are&nbsp;numbers,&nbsp;return
071&nbsp;&nbsp;&nbsp;&nbsp;&quot;Where&nbsp;`a`&nbsp;and&nbsp;`b`&nbsp;are&nbsp;both&nbsp;maps&nbsp;all&nbsp;of&nbsp;whose&nbsp;values&nbsp;are&nbsp;numbers,&nbsp;return
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;map&nbsp;whose&nbsp;keys&nbsp;are&nbsp;a&nbsp;union&nbsp;of&nbsp;the&nbsp;keys&nbsp;of&nbsp;`a`&nbsp;and&nbsp;`b`&nbsp;and&nbsp;whose&nbsp;values
072&nbsp;&nbsp;&nbsp;&nbsp;a&nbsp;map&nbsp;whose&nbsp;keys&nbsp;are&nbsp;a&nbsp;union&nbsp;of&nbsp;the&nbsp;keys&nbsp;of&nbsp;`a`&nbsp;and&nbsp;`b`&nbsp;and&nbsp;whose&nbsp;values
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;the&nbsp;sums&nbsp;of&nbsp;their&nbsp;respective&nbsp;values.&quot;
073&nbsp;&nbsp;&nbsp;&nbsp;are&nbsp;the&nbsp;sums&nbsp;of&nbsp;their&nbsp;respective&nbsp;values.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;[a&nbsp;b]
074&nbsp;&nbsp;&nbsp;&nbsp;[a&nbsp;b]
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;(reduce
<span class="covered" title="2 out of 2 forms covered">
075&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
068&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merge
<span class="covered" title="1 out of 1 forms covered">
076&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merge
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
069&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a
<span class="covered" title="1 out of 1 forms covered">
077&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
070&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
<span class="covered" title="3 out of 3 forms covered">
078&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="not-covered" title="0 out of 21 forms covered">
071&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(hash-map&nbsp;%&nbsp;(+&nbsp;(or&nbsp;(a&nbsp;%)&nbsp;0)&nbsp;(or&nbsp;(b&nbsp;%)&nbsp;0)))
<span class="partial" title="20 out of 21 forms covered">
079&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(hash-map&nbsp;%&nbsp;(+&nbsp;(or&nbsp;(a&nbsp;%)&nbsp;0)&nbsp;(or&nbsp;(b&nbsp;%)&nbsp;0)))
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
072&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keys&nbsp;b))))
<span class="covered" title="3 out of 3 forms covered">
080&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keys&nbsp;b))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
073&nbsp;&nbsp;
081&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
074&nbsp;&nbsp;(defn&nbsp;add-known-prices
082&nbsp;&nbsp;(defn&nbsp;add-known-prices
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
075&nbsp;&nbsp;&nbsp;&nbsp;&quot;Add&nbsp;the&nbsp;current&nbsp;prices&nbsp;at&nbsp;this&nbsp;`merchant`&#x27;s&nbsp;location&nbsp;in&nbsp;the&nbsp;`world`
083&nbsp;&nbsp;&nbsp;&nbsp;&quot;Add&nbsp;the&nbsp;current&nbsp;prices&nbsp;at&nbsp;this&nbsp;`merchant`&#x27;s&nbsp;location&nbsp;in&nbsp;the&nbsp;`world`
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
076&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;a&nbsp;new&nbsp;cacke&nbsp;of&nbsp;known&nbsp;prices,&nbsp;and&nbsp;return&nbsp;it.&quot;
084&nbsp;&nbsp;&nbsp;&nbsp;to&nbsp;a&nbsp;new&nbsp;cache&nbsp;of&nbsp;known&nbsp;prices,&nbsp;and&nbsp;return&nbsp;it.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
077&nbsp;&nbsp;&nbsp;&nbsp;[merchant&nbsp;world]
085&nbsp;&nbsp;&nbsp;&nbsp;[merchant&nbsp;world]
</span><br/>
<span class="not-covered" title="0 out of 4 forms covered">
078&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[m&nbsp;(cond
086&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[m&nbsp;(cond
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
079&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;merchant)
087&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(keyword?&nbsp;merchant)
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
080&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:merchants&nbsp;merchant)
088&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:merchants&nbsp;merchant)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
081&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;merchant)
089&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;merchant)
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
082&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merchant)
090&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merchant)
</span><br/>
<span class="not-covered" title="0 out of 8 forms covered">
091&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k&nbsp;(or&nbsp;(:known-prices&nbsp;m)&nbsp;{})
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
083&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k&nbsp;(:known-prices&nbsp;m)
092&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l&nbsp;(:location&nbsp;m)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
084&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;l&nbsp;(:location&nbsp;m)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
085&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d&nbsp;(:date&nbsp;world)
<span class="not-covered" title="0 out of 8 forms covered">
093&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d&nbsp;(or&nbsp;(:date&nbsp;world)&nbsp;0)
</span><br/>
<span class="not-covered" title="0 out of 7 forms covered">
086&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;(-&gt;&nbsp;world&nbsp;:cities&nbsp;l&nbsp;:prices)]
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
087&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
088&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merge
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
089&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k
094&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;(-&gt;&nbsp;world&nbsp;:cities&nbsp;l&nbsp;:prices)]
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
090&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
095&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cond
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
096&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(nil?&nbsp;m)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
097&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw&nbsp;(Exception.&nbsp;&quot;No&nbsp;merchant?&quot;))
</span><br/>
<span class="not-covered" title="0 out of 14 forms covered">
098&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(or&nbsp;(nil?&nbsp;l)&nbsp;(nil?&nbsp;(-&gt;&nbsp;world&nbsp;:cities&nbsp;l)))
</span><br/>
<span class="not-covered" title="0 out of 6 forms covered">
099&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw&nbsp;(Exception.&nbsp;(str&nbsp;&quot;No&nbsp;known&nbsp;location&nbsp;for&nbsp;merchant&nbsp;&quot;&nbsp;m)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:else
</span><br/>
<span class="not-covered" title="0 out of 2 forms covered">
101&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
102&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merge
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
103&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
104&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="not-covered" title="0 out of 17 forms covered">
091&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(hash-map&nbsp;%&nbsp;(apply&nbsp;vector&nbsp;cons&nbsp;{:price&nbsp;(p&nbsp;%)&nbsp;:date&nbsp;d}&nbsp;(k&nbsp;%)))
105&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(hash-map&nbsp;%&nbsp;(apply&nbsp;vector&nbsp;cons&nbsp;{:price&nbsp;(p&nbsp;%)&nbsp;:date&nbsp;d}&nbsp;(k&nbsp;%)))
</span><br/>
<span class="not-covered" title="0 out of 5 forms covered">
092&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:commodities&nbsp;keys)))))
106&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&gt;&nbsp;world&nbsp;:commodities&nbsp;keys))))))
</span><br/>
</body>
</html>

View file

@ -0,0 +1,41 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../../coverage.css"/> <title> the_great_game/objects/container.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;the-great-game.objects.container
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;(:require
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[the-great-game.objects.game-object&nbsp;:refer&nbsp;:all]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
004&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
005&nbsp;&nbsp;(defprotocol&nbsp;ProtoContainer
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;&nbsp;&nbsp;(contents
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[container]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;sequence&nbsp;of&nbsp;the&nbsp;contents&nbsp;of&nbsp;this&nbsp;`container`,&nbsp;or&nbsp;`nil`&nbsp;if&nbsp;empty.&quot;)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;&nbsp;&nbsp;(is-empty?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[container]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;`true`&nbsp;if&nbsp;this&nbsp;`container`&nbsp;is&nbsp;empty,&nbsp;else&nbsp;`false`.&quot;))
</span><br/>
</body>
</html>

View file

@ -0,0 +1,65 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../../coverage.css"/> <title> the_great_game/objects/game_object.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;the-great-game.objects.game-object
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&quot;Anything&nbsp;at&nbsp;all&nbsp;in&nbsp;the&nbsp;game&nbsp;world&quot;)
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
003&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
004&nbsp;&nbsp;(defprotocol&nbsp;ProtoObject
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;&nbsp;&nbsp;&quot;An&nbsp;object&nbsp;in&nbsp;the&nbsp;world&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;&nbsp;&nbsp;(id&nbsp;[object]&nbsp;&quot;Returns&nbsp;the&nbsp;unique&nbsp;id&nbsp;of&nbsp;this&nbsp;object.&quot;)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;&nbsp;&nbsp;(reify-object
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[object]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;Adds&nbsp;this&nbsp;`object`&nbsp;to&nbsp;the&nbsp;global&nbsp;object&nbsp;list.&nbsp;If&nbsp;the&nbsp;`object`&nbsp;has&nbsp;a
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;non-nil&nbsp;value&nbsp;for&nbsp;its&nbsp;`id`&nbsp;method,&nbsp;keys&nbsp;it&nbsp;to&nbsp;that&nbsp;id&nbsp;-&nbsp;**but**&nbsp;if&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id&nbsp;value&nbsp;is&nbsp;already&nbsp;in&nbsp;use,&nbsp;throws&nbsp;a&nbsp;hard&nbsp;exception.&nbsp;Returns&nbsp;the&nbsp;id&nbsp;to
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;which&nbsp;the&nbsp;object&nbsp;is&nbsp;keyed&nbsp;in&nbsp;the&nbsp;global&nbsp;object&nbsp;list.&quot;))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
013&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
014&nbsp;&nbsp;(defrecord&nbsp;GameObject
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;&nbsp;&nbsp;[id]
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;&quot;An&nbsp;object&nbsp;in&nbsp;the&nbsp;world&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;&nbsp;&nbsp;ProtoObject
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
018&nbsp;&nbsp;&nbsp;&nbsp;(id&nbsp;[_]&nbsp;id)
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
019&nbsp;&nbsp;&nbsp;&nbsp;(reify-object&nbsp;[object]&nbsp;&quot;TODO:&nbsp;doesn&#x27;t&nbsp;work&nbsp;yet&quot;))
</span><br/>
</body>
</html>

View file

@ -0,0 +1,440 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../coverage.css"/> <title> the_great_game/time.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;the-great-game.time
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[clojure.string&nbsp;:as&nbsp;s]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
003&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
004&nbsp;&nbsp;(def&nbsp;game-start-time
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;&nbsp;&nbsp;&quot;The&nbsp;start&nbsp;time&nbsp;of&nbsp;this&nbsp;run.&quot;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
006&nbsp;&nbsp;&nbsp;&nbsp;(System&#x2F;currentTimeMillis))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
007&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
008&nbsp;&nbsp;(def&nbsp;^:const&nbsp;game-day-length
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;&nbsp;&nbsp;&quot;The&nbsp;Java&nbsp;clock&nbsp;advances&nbsp;in&nbsp;milliseconds,&nbsp;which&nbsp;is&nbsp;fine.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
010&nbsp;&nbsp;&nbsp;&nbsp;But&nbsp;we&nbsp;need&nbsp;game-days&nbsp;to&nbsp;be&nbsp;shorter&nbsp;than&nbsp;real&nbsp;world&nbsp;days.
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;Witcher&nbsp;3&nbsp;game&nbsp;day&nbsp;is&nbsp;1&nbsp;hour&nbsp;36&nbsp;minutes,&nbsp;or&nbsp;96&nbsp;minutes,&nbsp;which&nbsp;is
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;&nbsp;&nbsp;presumably&nbsp;researched.&nbsp;Round&nbsp;it&nbsp;up&nbsp;to&nbsp;100&nbsp;minutes&nbsp;for&nbsp;easier
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;&nbsp;&nbsp;calculation.&quot;
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
014&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;minutes&nbsp;per&nbsp;game&nbsp;day
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;60&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;seconds&nbsp;per&nbsp;minute
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1000))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;milliseconds&nbsp;per&nbsp;second
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
017&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
018&nbsp;&nbsp;(defn&nbsp;now
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;&nbsp;&nbsp;&quot;For&nbsp;now,&nbsp;we&#x27;ll&nbsp;use&nbsp;Java&nbsp;timestamp&nbsp;for&nbsp;time;&nbsp;ultimately,&nbsp;we&nbsp;need&nbsp;a
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
020&nbsp;&nbsp;&nbsp;&nbsp;concept&nbsp;of&nbsp;game-time&nbsp;which&nbsp;allows&nbsp;us&nbsp;to&nbsp;drive&nbsp;day&#x2F;night&nbsp;cycle,&nbsp;seasons,
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
021&nbsp;&nbsp;&nbsp;&nbsp;et&nbsp;cetera,&nbsp;but&nbsp;what&nbsp;matters&nbsp;about&nbsp;time&nbsp;is&nbsp;that&nbsp;it&nbsp;is&nbsp;a&nbsp;value&nbsp;which
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
022&nbsp;&nbsp;&nbsp;&nbsp;increases.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
023&nbsp;&nbsp;&nbsp;&nbsp;[]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
024&nbsp;&nbsp;&nbsp;&nbsp;(System&#x2F;currentTimeMillis))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
025&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
026&nbsp;&nbsp;(def&nbsp;^:const&nbsp;canonical-ordering-of-houses
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
027&nbsp;&nbsp;&nbsp;&nbsp;&quot;The&nbsp;canonical&nbsp;ordering&nbsp;of&nbsp;religious&nbsp;houses.&quot;
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;[:eye
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:foot
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:nose
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:hand
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:ear
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:mouth
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
034&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:stomach
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:furrow
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
036&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:plough])
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
037&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
038&nbsp;&nbsp;(def&nbsp;^:const&nbsp;days-of-week
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;&quot;The&nbsp;eight-day&nbsp;week&nbsp;of&nbsp;the&nbsp;game&nbsp;world.&nbsp;This&nbsp;differs&nbsp;from&nbsp;the&nbsp;canonical
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;ordering&nbsp;of&nbsp;houses&nbsp;in&nbsp;that&nbsp;it&nbsp;omits&nbsp;the&nbsp;eye.&quot;
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;(rest&nbsp;canonical-ordering-of-houses))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
042&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
043&nbsp;&nbsp;(def&nbsp;^:const&nbsp;days-in-week
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;&quot;This&nbsp;world&nbsp;has&nbsp;an&nbsp;eight&nbsp;day&nbsp;week.&quot;
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;(count&nbsp;days-of-week))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
046&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
047&nbsp;&nbsp;(def&nbsp;^:const&nbsp;seasons-of-year
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
048&nbsp;&nbsp;&nbsp;&nbsp;&quot;The&nbsp;ordering&nbsp;of&nbsp;seasons&nbsp;in&nbsp;the&nbsp;year&nbsp;is&nbsp;different&nbsp;from&nbsp;the&nbsp;canonical
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;ordering&nbsp;of&nbsp;the&nbsp;houses,&nbsp;for&nbsp;reasons&nbsp;of&nbsp;the&nbsp;agricultural&nbsp;cycle.&quot;
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;[:foot
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:nose
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:hand
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:ear
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:mouth
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
055&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:stomach
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
056&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:plough
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:furrow
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:eye])
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
059&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
060&nbsp;&nbsp;(def&nbsp;^:const&nbsp;seasons-in-year
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;&quot;Nine&nbsp;seasons&nbsp;in&nbsp;a&nbsp;year,&nbsp;one&nbsp;for&nbsp;each&nbsp;house&nbsp;(although&nbsp;the&nbsp;order&nbsp;is
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;different.&quot;
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;(count&nbsp;seasons-of-year))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
064&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
065&nbsp;&nbsp;(def&nbsp;^:const&nbsp;weeks-of-season
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;&quot;To&nbsp;fit&nbsp;nine&nbsp;seasons&nbsp;of&nbsp;eight&nbsp;day&nbsp;weeks&nbsp;into&nbsp;365&nbsp;days,&nbsp;each&nbsp;must&nbsp;be&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;five&nbsp;weeks.&quot;
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
068&nbsp;&nbsp;&nbsp;&nbsp;[:first&nbsp;:second&nbsp;:third&nbsp;:fourth&nbsp;:fifth])
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
069&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
070&nbsp;&nbsp;(def&nbsp;^:const&nbsp;weeks-in-season
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
071&nbsp;&nbsp;&nbsp;&nbsp;&quot;To&nbsp;fit&nbsp;nine&nbsp;seasons&nbsp;of&nbsp;eight&nbsp;day&nbsp;weeks&nbsp;into&nbsp;365&nbsp;days,&nbsp;each&nbsp;must&nbsp;be&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
072&nbsp;&nbsp;&nbsp;&nbsp;five&nbsp;weeks.&quot;
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
073&nbsp;&nbsp;&nbsp;&nbsp;(count&nbsp;weeks-of-season))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
074&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
075&nbsp;&nbsp;(def&nbsp;^:const&nbsp;days-in-season
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
076&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;weeks-in-season&nbsp;days-in-week))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
077&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
078&nbsp;&nbsp;(defn&nbsp;game-time
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
079&nbsp;&nbsp;&nbsp;&nbsp;&quot;With&nbsp;no&nbsp;arguments,&nbsp;the&nbsp;current&nbsp;game&nbsp;time.&nbsp;If&nbsp;a&nbsp;Java&nbsp;`timestamp`&nbsp;value&nbsp;is
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
080&nbsp;&nbsp;&nbsp;&nbsp;passed&nbsp;(as&nbsp;a&nbsp;`long`),&nbsp;the&nbsp;game&nbsp;time&nbsp;represented&nbsp;by&nbsp;that&nbsp;value.&quot;
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
081&nbsp;&nbsp;&nbsp;&nbsp;([]&nbsp;(game-time&nbsp;(now)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
082&nbsp;&nbsp;&nbsp;&nbsp;([timestamp]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
083&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-&nbsp;timestamp&nbsp;game-start-time)))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
084&nbsp;&nbsp;
</span><br/>
<span class="covered" title="63 out of 63 forms covered">
085&nbsp;&nbsp;(defmacro&nbsp;day-of-year
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
086&nbsp;&nbsp;&nbsp;&nbsp;&quot;The&nbsp;day&nbsp;of&nbsp;the&nbsp;year&nbsp;represented&nbsp;by&nbsp;this&nbsp;`game-time`,&nbsp;ignoring&nbsp;leap&nbsp;years.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
087&nbsp;&nbsp;&nbsp;&nbsp;[game-time]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
088&nbsp;&nbsp;&nbsp;&nbsp;`(mod&nbsp;(long&nbsp;(&#x2F;&nbsp;~game-time&nbsp;game-day-length))&nbsp;365))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
089&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
090&nbsp;&nbsp;(def&nbsp;waiting-day?
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
091&nbsp;&nbsp;&nbsp;&nbsp;&quot;Does&nbsp;this&nbsp;`game-time`&nbsp;represent&nbsp;a&nbsp;waiting&nbsp;day?&quot;
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
092&nbsp;&nbsp;&nbsp;&nbsp;(memoize
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
093&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;we&#x27;re&nbsp;likely&nbsp;to&nbsp;call&nbsp;this&nbsp;several&nbsp;times&nbsp;in&nbsp;quick&nbsp;succession&nbsp;on&nbsp;the
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
094&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;;&nbsp;same&nbsp;timestamp
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
095&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(fn&nbsp;[game-time]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
096&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(&gt;=
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
097&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(day-of-year&nbsp;game-time)
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
098&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*&nbsp;seasons-in-year&nbsp;weeks-in-season&nbsp;days-in-week)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
099&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
100&nbsp;&nbsp;(defn&nbsp;day
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
101&nbsp;&nbsp;&nbsp;&nbsp;&quot;Day&nbsp;of&nbsp;the&nbsp;eight-day&nbsp;week&nbsp;represented&nbsp;by&nbsp;this&nbsp;`game-time`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
102&nbsp;&nbsp;&nbsp;&nbsp;[game-time]
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
103&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[day-of-week&nbsp;(mod&nbsp;(day-of-year&nbsp;game-time)&nbsp;days-in-week)]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
104&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(waiting-day?&nbsp;game-time)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
105&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(nth&nbsp;weeks-of-season&nbsp;day-of-week)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
106&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(nth&nbsp;days-of-week&nbsp;day-of-week))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
107&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
108&nbsp;&nbsp;(defn&nbsp;week
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
109&nbsp;&nbsp;&nbsp;&nbsp;&quot;Week&nbsp;of&nbsp;season&nbsp;represented&nbsp;by&nbsp;this&nbsp;`game-time`.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
110&nbsp;&nbsp;&nbsp;&nbsp;[game-time]
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
111&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[day-of-season&nbsp;(mod&nbsp;(day-of-year&nbsp;game-time)&nbsp;days-in-season)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
112&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;week&nbsp;(&#x2F;&nbsp;day-of-season&nbsp;days-in-week)]
</span><br/>
<span class="partial" title="4 out of 5 forms covered">
113&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(waiting-day?&nbsp;game-time)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
114&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:waiting
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
115&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(nth&nbsp;weeks-of-season&nbsp;week))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
116&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
117&nbsp;&nbsp;(defn&nbsp;season
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
118&nbsp;&nbsp;&nbsp;&nbsp;[game-time]
</span><br/>
<span class="covered" title="11 out of 11 forms covered">
119&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[season&nbsp;(int&nbsp;(&#x2F;&nbsp;(day-of-year&nbsp;game-time)&nbsp;days-in-season))]
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
120&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if&nbsp;(waiting-day?&nbsp;game-time)
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
121&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:waiting
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
122&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(nth&nbsp;seasons-of-year&nbsp;season))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
123&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
124&nbsp;&nbsp;(defn&nbsp;date-string
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
125&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;correctly&nbsp;formatted&nbsp;date&nbsp;for&nbsp;this&nbsp;`game-time`&nbsp;in&nbsp;the&nbsp;calendar&nbsp;of
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
126&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;Great&nbsp;Place.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
127&nbsp;&nbsp;&nbsp;&nbsp;[game-time]
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
128&nbsp;&nbsp;&nbsp;&nbsp;(s&#x2F;join
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
129&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;&nbsp;&quot;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
130&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
131&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(waiting-day?&nbsp;game-time)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
132&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[(s&#x2F;capitalize
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
133&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(name
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
134&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(nth
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
135&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;weeks-of-season
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
136&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(mod&nbsp;(day-of-year&nbsp;game-time)&nbsp;days-in-week))))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
137&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;waiting&nbsp;day&quot;]
</span><br/>
<span class="covered" title="9 out of 9 forms covered">
138&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[(s&#x2F;capitalize&nbsp;(name&nbsp;(week&nbsp;game-time)))
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
139&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(s&#x2F;capitalize&nbsp;(name&nbsp;(day&nbsp;game-time)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
140&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;of&nbsp;the&quot;
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
141&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(s&#x2F;capitalize&nbsp;(name&nbsp;(season&nbsp;game-time)))])))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
142&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
143&nbsp;&nbsp;
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
144&nbsp;&nbsp;
</span><br/>
</body>
</html>

View file

@ -0,0 +1,119 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="../../coverage.css"/> <title> the_great_game/world/location.clj </title>
</head>
<body>
<span class="covered" title="1 out of 1 forms covered">
001&nbsp;&nbsp;(ns&nbsp;the-great-game.world.location
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&quot;Functions&nbsp;dealing&nbsp;with&nbsp;location&nbsp;in&nbsp;the&nbsp;world.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[clojure.math.numeric-tower&nbsp;:refer&nbsp;[expt&nbsp;sqrt]]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
004&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;A&nbsp;&#x27;location&#x27;&nbsp;value&nbsp;is&nbsp;a&nbsp;list&nbsp;comprising&nbsp;at&nbsp;most&nbsp;the&nbsp;x&#x2F;y&nbsp;coordinate&nbsp;location
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
006&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;and&nbsp;the&nbsp;ids&nbsp;of&nbsp;the&nbsp;settlement&nbsp;and&nbsp;region&nbsp;(possibly&nbsp;hierarchically)&nbsp;that&nbsp;contain
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;the&nbsp;location.&nbsp;If&nbsp;the&nbsp;x&#x2F;y&nbsp;is&nbsp;not&nbsp;local&nbsp;to&nbsp;the&nbsp;home&nbsp;of&nbsp;the&nbsp;receiving&nbsp;agent,&nbsp;they
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;won&#x27;t&nbsp;remember&nbsp;it&nbsp;and&nbsp;won&#x27;t&nbsp;pass&nbsp;it&nbsp;on;&nbsp;if&nbsp;any&nbsp;of&nbsp;the&nbsp;ids&nbsp;are&nbsp;not&nbsp;interesting
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;So&nbsp;location&nbsp;information&nbsp;will&nbsp;degrade&nbsp;progressively&nbsp;as&nbsp;the&nbsp;item&nbsp;is&nbsp;passed&nbsp;along.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
010&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
011&nbsp;&nbsp;;;&nbsp;&nbsp;&nbsp;It&nbsp;is&nbsp;assumed&nbsp;that&nbsp;the&nbsp;`:home`&nbsp;of&nbsp;a&nbsp;character&nbsp;is&nbsp;a&nbsp;location&nbsp;in&nbsp;this&nbsp;sense.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
012&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
013&nbsp;&nbsp;(defn&nbsp;get-coords
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;the&nbsp;coordinates&nbsp;in&nbsp;the&nbsp;game&nbsp;world&nbsp;of&nbsp;`location`,&nbsp;which&nbsp;may&nbsp;be
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
015&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;A&nbsp;coordinate&nbsp;pair&nbsp;in&nbsp;the&nbsp;format&nbsp;{:x&nbsp;5&nbsp;:y&nbsp;32};
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
016&nbsp;&nbsp;&nbsp;&nbsp;2.&nbsp;A&nbsp;location,&nbsp;as&nbsp;discussed&nbsp;above;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
017&nbsp;&nbsp;&nbsp;&nbsp;3.&nbsp;Any&nbsp;other&nbsp;gameworld&nbsp;object,&nbsp;having&nbsp;a&nbsp;`:location`&nbsp;property&nbsp;whose&nbsp;value
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;is&nbsp;one&nbsp;of&nbsp;the&nbsp;above.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
019&nbsp;&nbsp;&nbsp;&nbsp;[location]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
020&nbsp;&nbsp;&nbsp;&nbsp;(cond
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
021&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(empty?&nbsp;location)&nbsp;nil
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map?&nbsp;location)
</span><br/>
<span class="partial" title="1 out of 3 forms covered">
023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cond
</span><br/>
<span class="partial" title="13 out of 14 forms covered">
024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(and&nbsp;(number?&nbsp;(:x&nbsp;location))&nbsp;(number?&nbsp;(:y&nbsp;location)))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;location
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:location&nbsp;location)
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:location&nbsp;location))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:else
</span><br/>
<span class="covered" title="8 out of 8 forms covered">
029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(get-coords&nbsp;(first&nbsp;(remove&nbsp;keyword?&nbsp;location)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
030&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
031&nbsp;&nbsp;(defn&nbsp;distance-between
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;[location-1&nbsp;location-2]
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
033&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[c1&nbsp;(get-coords&nbsp;location-1)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
034&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c2&nbsp;(get-coords&nbsp;location-2)]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(when
</span><br/>
<span class="partial" title="5 out of 6 forms covered">
036&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(and&nbsp;c1&nbsp;c2)
</span><br/>
<span class="covered" title="26 out of 26 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(sqrt&nbsp;(+&nbsp;(expt&nbsp;(-&nbsp;(:x&nbsp;c1)&nbsp;(:x&nbsp;c2))&nbsp;2)&nbsp;(expt&nbsp;(-&nbsp;(:y&nbsp;c1)&nbsp;(:y&nbsp;c2))&nbsp;2))))))
</span><br/>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

9
docs/codox/on-dying.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

39
docs/codox/sandbox.html Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,14 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>The Great Game: Dcocumentation</title>
<title>The Great Game: Documentation</title>
<link rel="stylesheet" type="text/css" href="codox/css/default.css" />
</head>
<body>
<h1>The Great Game: Dcocumentation</h1>
<h1>The Great Game: Documentation</h1>
<ul>
<li><a href="cloverage">Test coverage</a></li>
<li><a href="codox">Primary documentaion</a></li>
<li><a href="codox/index.html">Primary documentaion</a></li>
<li><a href="cloverage/index.html">Test coverage</a></li>
</ul>
</body>
</html>

View file

@ -1,9 +1,10 @@
(defproject the-great-game "0.1.1-SNAPSHOT"
(defproject the-great-game "0.1.1"
:cloverage {:output "docs/cloverage"}
:codox {:metadata {:doc "**TODO**: write docs"
:doc/format :markdown}
:output-path "docs/codox"
:source-uri "https://github.com/simon-brooke/the-great-game/blob/master/{filepath}#L{line}"}
:cucumber-feature-paths ["test/features/"]
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/math.numeric-tower "0.0.4"]
[environ "1.1.0"]
@ -12,7 +13,9 @@
:license {:name "GNU General Public License,version 2.0 or (at your option) any later version"
:url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"}
:plugins [[lein-cloverage "1.1.1"]
[lein-codox "0.10.7"]]
[lein-codox "0.10.7"]
[lein-cucumber "1.0.2"]
[lein-gorilla "0.4.0"]]
:release-tasks [["vcs" "assert-committed"]
["change" "version" "leiningen.release/bump-version" "release"]

View file

@ -0,0 +1,43 @@
(ns the-great-game.agent.agent
"Anything in the game world with agency"
(:require [the-great-game.objects.game-object :refer [ProtoObject]]
[the-great-game.objects.container :refer [ProtoContainer]]))
;; hierarchy of needs probably gets implemented here
;; I'm probably going to want to defprotocol stuff, to define the hierarchy
;; of things in the gameworld; either that or drop to Java, wich I'd rather not do.
(defprotocol ProtoAgent
"An object which can act in the world"
(act
[actor world circle]
"Allow `actor` to do something in this `world`, in the context of this
`circle`; return the new state of the actor if something was done, `nil`
if nothing was done. Circle is expected to be one of
* `:active` - actors within visual/audible range of the player
character;
* `:pending` - actors not in the active circle, but sufficiently close
to it that they may enter the active circle within a short period;
* `:background` - actors who are active in the background in order to
handle trade, news, et cetera;
* `other` - actors who are not members of any other circle, although
I'm not clear whether it would ever be appropriate to invoke an
`act` method on them.
The `act` method *must not* have side effects; it must *only* return a
new state. If the actor's intention is to seek to change the state of
something else in the game world, it must add a representation of that
intention to the sequence which will be returned by its
`pending-intentions` method.")
(pending-intentions
[actor]
"Returns a sequence of effects an actor intends, as a consequence of
acting. The encoding of these is not yet defined."))
;; (defrecord Agent
;; "A default agent."
;; ProtoObject
;; ProtoContainer
;; ProtoAgent
;; )

View file

@ -1,6 +0,0 @@
(ns the-great-game.core)
(defn foo
"I don't do a whole lot."
[x]
(println x "Hello, World!"))

View file

@ -1,6 +1,7 @@
(ns the-great-game.gossip.gossip
"Interchange of news events between gossip agents"
(:require [the-great-game.utils :refer [deep-merge]]))
(:require [the-great-game.utils :refer [deep-merge]]
[the-great-game.gossip.news-items :refer [learn-news-item]]))
;; Note that habitual travellers are all gossip agents; specifically, at this
;; stage, that means merchants. When merchants are moved we also need to

View file

@ -1,6 +1,7 @@
(ns the-great-game.gossip.news-items
"Categories of news events interesting to gossip agents"
(:require [clojure.math.numeric-tower :refer [expt sqrt]]))
(:require [the-great-game.world.location :refer [distance-between]]
[the-great-game.time :refer [game-time]]))
;; The ideas here are based on the essay 'The spread of knowledge in a large
;; game world', q.v.; they've advanced a little beyond that and will doubtless
@ -20,14 +21,31 @@
their `verbs`. The `keys` associated with each topic are the extra pieces
of information required to give context to a gossip item. Generally:
* `actor` is the id of the character who performed the action;
* `other` is the id of the character on whom the action was performed;
* `actor` is the id of the character who it is reported performed the
action;
* `other` is the id of the character on whom it is reported the action
was performed;
* `location` is the place at which the action was performed;
* `object` is an object (or possibly list of objects?) relevant to the action;
* `object` is an object (or possibly list of objects?) relevant to the
action;
* `price` is special to buy/sell, but of significant interest to merchants.
#### Notes:
##### Characters:
*TODO* but note that at most all the receiver can learn about a character
from a news item is what the giver knows about that character, degraded by
what the receiver finds interesting about them. If we just pass the id here,
then either the receiver knows everything in the database about the
character, or else the receiver knows nothing at all about the character.
Neither is desirable. Further thought needed.
By implication, the character values passed should include *all* the
information the giver knows about the character; that can then be degraded
as the receiver stores only that segment which the receiver finds
interesting.
##### Locations:
A 'location' value is a list comprising at most the x/y coordinate location
@ -47,7 +65,7 @@
{ ;; A significant attack is interesting whether or not it leads to deaths
:attack {:verb :attack :keys [:actor :other :location]}
;; Deaths of characters may be interesting
:die {:verb :attack :keys [:actor :location]}
:die {:verb :die :keys [:actor :location]}
;; Deliberate killings are interesting.
:kill {:verb :kill :keys [:actor :other :location]
:inferences [{:verb :die :actor :other :other :nil}]}
@ -62,6 +80,7 @@
:plot {:verb :plot :keys [:actor :other :object :location]}
;; Rapes are interesting.
:rape {:verb :rape :keys [:actor :other :location]
;; Should you also infer from rape that actor is male and adult?
:inferences [{:verb :attack}
{:verb :sex}
{:verb :sex :actor :other :other :actor}]}
@ -84,7 +103,9 @@
(defn interest-in-character
"Integer representation of how interesting this `character` is to this
`gossip`."
`gossip`.
*TODO:* this assumes that characters are passed as keywords, but, as
documented above, they probably have to be maps, to allow for degradation."
[gossip character]
(count
(concat
@ -97,98 +118,47 @@
[gossip character]
(> (interest-in-character gossip character) 0))
(defn get-coords
"Return the coordinates in the game world of `location`, which may be
1. A coordinate pair in the format {:x 5 :y 32};
2. A location, as discussed above;
3. Any other gameworld object, having a `:location` property whose value
is one of the above."
[location]
(cond
(empty? location) nil
(map? location)
(cond
(and (number? (:x location)) (number? (:y location)))
location
(:location location)
(:location location))
:else
(get-coords (first (remove keyword? location)))))
;; (get-coords {:x 5 :y 7})
;; (get-coords [{:x -4 :y 55} :auchencairn :galloway :scotland])
(defn distance-between
[location-1 location-2]
(let [c1 (get-coords location-1)
c2 (get-coords location-2)]
(if
(and c1 c2)
(sqrt (+ (expt (- (:x c1) (:x c2)) 2) (expt (- (:y c1) (:y c2)) 2))))))
;; (distance-between {:x 5 :y 5} {:x 2 :y 2})
;; (distance-between {:x 5 :y 5} {:x 2 :y 5})
;; (distance-between {:x 5 :y 5} [{:x -4 :y 55} :auchencairn :galloway :scotland])
;; (distance-between {:x 5 :y 5} [:auchencairn :galloway :scotland])
(defn interest-in-location
"Integer representation of how interesting this `location` is to this
`gossip`."
[gossip location]
(cond
(and (map? location) (number? (:x location)) (number? (:y location)))
(if-let [home (:home gossip)]
(let [d (distance-between location home)
i (/ 10000 d) ;; 10000 at metre scale is 10km; interest should
;;fall off with distance from home, but possibly on a log scale
]
(if (> i 1) i 0))
0)
(coll? location)
(reduce
+
(map
#(interest-in-location gossip %)
location))
(and (map? location) (:x location) (:y location))
(if-let [home (:home gossip)]
(let [d (distance-between location home)
i (/ 10000 d) ;; 10000 at metre scale is 10km; interest should
;;fall of with distance from home, but possibly on a log scale
]
(if (i > 1) i 0)
i))
:else
(count
(filter
#(some (fn [x] (= x location)) (:location %))
(:knowledge gossip)))))
(cons {:location (:home gossip)} (:knowledge gossip))))))
;; (interest-in-location
;; {:knowledge [{:verb :steal
;; :actor :albert
;; :other :belinda
;; :object :foo
;; :location [{:x 35 :y 23} :auchencairn :galloway]}]}
;; :galloway)
;; (interest-in-location
;; {:knowledge [{:verb :steal
;; :actor :albert
;; :other :belinda
;; :object :foo
;; :location [{:x 35 :y 23} :auchencairn :galloway]}]}
;; [:galloway :scotland])
;; (interest-in-location
;; {:knowledge [{:verb :steal
;; :actor :albert
;; :other :belinda
;; :object :foo
;; :location [{:x 35 :y 23} :auchencairn :galloway]}]}
;; :dumfries)
;; (interest-in-location
;; {:home {:x 35 :y 23}}
;; {:x 35 :y 24})
;; (interest-in-location {:home [{0, 0} :test-home] :knowledge []} [:test-home])
(defn interesting-location?
"True if the location of this news `item` is interesting to this `gossip`."
[gossip item]
(> (interest-in-location gossip (:location item)) 1))
(> (interest-in-location gossip (:location item)) 0))
(defn interesting-object?
[gossip object]
;; TODO: Not yet (really) implemented
true)
(defn interesting-topic?
[gossip topic]
;; TODO: Not yet (really) implemented
true)
(defn interesting-item?
"True if anything about this news `item` is interesting to this `gossip`."
@ -212,40 +182,75 @@
#(= % :verb)
(keys rule))))))
;; (infer {:verb :marry :actor :adam :other :belinda}
;; {:verb :marry :actor :other :other :actor})
;; (infer {:verb :rape :actor :adam :other :belinda}
;; {:verb :attack})
;; (infer {:verb :rape :actor :adam :other :belinda}
;; {:verb :sex :actor :other :other :actor})
(declare learn-news-item)
(defn make-all-inferences
"Return a list of knowledge entries that can be inferred from this news
`item`."
[item]
(set
(reduce
concat
(map
#(:knowledge (learn-news-item {} (infer item %) false))
(:inferences (news-topics (:verb item)))))))
(defn degrade-character
"Return a character specification like this `character`, but comprising
only those properties this `gossip` is interested in."
[gossip character]
;; TODO: Not yet (really) implemented
character)
(defn degrade-location
"Return a location specification like this `location`, but comprising
only those elements this `gossip` is interested in. If none, return
`nil`."
[gossip location]
(let [l (if
(coll? location)
(filter
#(when (interesting-location? gossip %) %)
location))]
(when-not (empty? l) l)))
(defn learn-news-item
"Return a gossip like this `gossip`, which has learned this news `item` if
it is of interest to them."
;; TODO: Not yet implemented
([gossip item]
(learn-news-item gossip item false))
(learn-news-item gossip item true))
([gossip item follow-inferences?]
(if
(interesting-item? gossip item)
(let [g (assoc gossip :knowledge
(cons
(assoc
item
:nth-hand (if
(number? (:nth-hand item))
(inc (:nth-hand item))
1)
;; ought to degrate the location
;; ought to maybe-degrade characters we're not yet interested in
)
;; ought not to add knowledge items we already have, except
;; to replace if new item is of increased specificity
(:knowledge gossip)))]
(let
[g (assoc
gossip
:knowledge
(cons
(assoc
item
:nth-hand (if
(number? (:nth-hand item))
(inc (:nth-hand item))
1)
:time-stamp (if
(number? (:time-stamp item))
(:time-stamp item)
(game-time))
:location (degrade-location gossip (:location item))
;; TODO: ought to maybe-degrade characters we're not yet interested in
)
;; TODO: ought not to add knowledge items we already have, except
;; to replace if new item is of increased specificity
(:knowledge gossip)))]
(if follow-inferences?
(reduce
merge
(assoc
g
(map
#(learn-news-item gossip (infer item %) false)
(:inferences (news-topics (:verb item))))))))))
:knowledge
(concat (:knowledge g) (make-all-inferences item)))
g))
gossip)))

View file

@ -23,7 +23,7 @@
(-> world :merchants merchant)
(map? merchant)
merchant)
cargo (:stock m)]
cargo (or (:stock m) {})]
(reduce
+
0
@ -41,9 +41,11 @@
(-> world :merchants merchant)
(map? merchant)
merchant)]
(quot
(- (:capacity m) (burden m world))
(-> world :commodities commodity :weight))))
(max
0
(quot
(- (or (:capacity m) 0) (burden m world))
(-> world :commodities commodity :weight)))))
(defn can-afford
"Return the number of units of this `commodity` which this `merchant`
@ -55,9 +57,15 @@
(map? merchant)
merchant)
l (:location m)]
(quot
(:cash m)
(-> world :cities l :prices commodity))))
(cond
(nil? m)
(throw (Exception. "No merchant?"))
(or (nil? l) (nil? (-> world :cities l)))
(throw (Exception. (str "No known location for merchant " m)))
:else
(quot
(:cash m)
(-> world :cities l :prices commodity)))))
(defn add-stock
"Where `a` and `b` are both maps all of whose values are numbers, return
@ -73,20 +81,26 @@
(defn add-known-prices
"Add the current prices at this `merchant`'s location in the `world`
to a new cacke of known prices, and return it."
to a new cache of known prices, and return it."
[merchant world]
(let [m (cond
(keyword? merchant)
(-> world :merchants merchant)
(map? merchant)
merchant)
k (:known-prices m)
k (or (:known-prices m) {})
l (:location m)
d (:date world)
d (or (:date world) 0)
p (-> world :cities l :prices)]
(reduce
merge
k
(map
#(hash-map % (apply vector cons {:price (p %) :date d} (k %)))
(-> world :commodities keys)))))
(cond
(nil? m)
(throw (Exception. "No merchant?"))
(or (nil? l) (nil? (-> world :cities l)))
(throw (Exception. (str "No known location for merchant " m)))
:else
(reduce
merge
k
(map
#(hash-map % (apply vector cons {:price (p %) :date d} (k %)))
(-> world :commodities keys))))))

View file

@ -0,0 +1,11 @@
(ns the-great-game.objects.container
(:require
[the-great-game.objects.game-object :refer :all]))
(defprotocol ProtoContainer
(contents
[container]
"Return a sequence of the contents of this `container`, or `nil` if empty.")
(is-empty?
[container]
"Return `true` if this `container` is empty, else `false`."))

View file

@ -0,0 +1,19 @@
(ns the-great-game.objects.game-object
"Anything at all in the game world")
(defprotocol ProtoObject
"An object in the world"
(id [object] "Returns the unique id of this object.")
(reify-object
[object]
"Adds this `object` to the global object list. If the `object` has a
non-nil value for its `id` method, keys it to that id - **but** if the
id value is already in use, throws a hard exception. Returns the id to
which the object is keyed in the global object list."))
(defrecord GameObject
[id]
;; "An object in the world"
ProtoObject
(id [_] id)
(reify-object [object] "TODO: doesn't work yet"))

144
src/the_great_game/time.clj Normal file
View file

@ -0,0 +1,144 @@
(ns the-great-game.time
(:require [clojure.string :as s]))
(def game-start-time
"The start time of this run."
(System/currentTimeMillis))
(def ^:const game-day-length
"The Java clock advances in milliseconds, which is fine.
But we need game-days to be shorter than real world days.
A Witcher 3 game day is 1 hour 36 minutes, or 96 minutes, which is
presumably researched. Round it up to 100 minutes for easier
calculation."
(* 100 ;; minutes per game day
60 ;; seconds per minute
1000)) ;; milliseconds per second
(defn now
"For now, we'll use Java timestamp for time; ultimately, we need a
concept of game-time which allows us to drive day/night cycle, seasons,
et cetera, but what matters about time is that it is a value which
increases."
[]
(System/currentTimeMillis))
(def ^:const canonical-ordering-of-houses
"The canonical ordering of religious houses."
[:eye
:foot
:nose
:hand
:ear
:mouth
:stomach
:furrow
:plough])
(def ^:const days-of-week
"The eight-day week of the game world. This differs from the canonical
ordering of houses in that it omits the eye."
(rest canonical-ordering-of-houses))
(def ^:const days-in-week
"This world has an eight day week."
(count days-of-week))
(def ^:const seasons-of-year
"The ordering of seasons in the year is different from the canonical
ordering of the houses, for reasons of the agricultural cycle."
[:foot
:nose
:hand
:ear
:mouth
:stomach
:plough
:furrow
:eye])
(def ^:const seasons-in-year
"Nine seasons in a year, one for each house (although the order is
different."
(count seasons-of-year))
(def ^:const weeks-of-season
"To fit nine seasons of eight day weeks into 365 days, each must be of
five weeks."
[:first :second :third :fourth :fifth])
(def ^:const weeks-in-season
"To fit nine seasons of eight day weeks into 365 days, each must be of
five weeks."
(count weeks-of-season))
(def ^:const days-in-season
(* weeks-in-season days-in-week))
(defn game-time
"With no arguments, the current game time. If a Java `timestamp` value is
passed (as a `long`), the game time represented by that value."
([] (game-time (now)))
([timestamp]
(- timestamp game-start-time)))
(defmacro day-of-year
"The day of the year represented by this `game-time`, ignoring leap years."
[game-time]
`(mod (long (/ ~game-time game-day-length)) 365))
(def waiting-day?
"Does this `game-time` represent a waiting day?"
(memoize
;; we're likely to call this several times in quick succession on the
;; same timestamp
(fn [game-time]
(>=
(day-of-year game-time)
(* seasons-in-year weeks-in-season days-in-week)))))
(defn day
"Day of the eight-day week represented by this `game-time`."
[game-time]
(let [day-of-week (mod (day-of-year game-time) days-in-week)]
(if (waiting-day? game-time)
(nth weeks-of-season day-of-week)
(nth days-of-week day-of-week))))
(defn week
"Week of season represented by this `game-time`."
[game-time]
(let [day-of-season (mod (day-of-year game-time) days-in-season)
week (/ day-of-season days-in-week)]
(if (waiting-day? game-time)
:waiting
(nth weeks-of-season week))))
(defn season
[game-time]
(let [season (int (/ (day-of-year game-time) days-in-season))]
(if (waiting-day? game-time)
:waiting
(nth seasons-of-year season))))
(defn date-string
"Return a correctly formatted date for this `game-time` in the calendar of
the Great Place."
[game-time]
(s/join
" "
(if
(waiting-day? game-time)
[(s/capitalize
(name
(nth
weeks-of-season
(mod (day-of-year game-time) days-in-week))))
"waiting day"]
[(s/capitalize (name (week game-time)))
(s/capitalize (name (day game-time)))
"of the"
(s/capitalize (name (season game-time)))])))

View file

@ -1,5 +1,6 @@
(ns the-great-game.world.location
"Functions dealing with location in the world.")
"Functions dealing with location in the world."
(:require [clojure.math.numeric-tower :refer [expt sqrt]]))
;; A 'location' value is a list comprising at most the x/y coordinate location
;; and the ids of the settlement and region (possibly hierarchically) that contain
@ -8,3 +9,29 @@
;; So location information will degrade progressively as the item is passed along.
;; It is assumed that the `:home` of a character is a location in this sense.
(defn get-coords
"Return the coordinates in the game world of `location`, which may be
1. A coordinate pair in the format {:x 5 :y 32};
2. A location, as discussed above;
3. Any other gameworld object, having a `:location` property whose value
is one of the above."
[location]
(cond
(empty? location) nil
(map? location)
(cond
(and (number? (:x location)) (number? (:y location)))
location
(:location location)
(:location location))
:else
(get-coords (first (remove keyword? location)))))
(defn distance-between
[location-1 location-2]
(let [c1 (get-coords location-1)
c2 (get-coords location-2)]
(when
(and c1 c2)
(sqrt (+ (expt (- (:x c1) (:x c2)) 2) (expt (- (:y c1) (:y c2)) 2))))))

View file

@ -0,0 +1,4 @@
(ns the-great-game.gossip.gossip-test
(:require [clojure.test :refer :all]
[the-great-game.gossip.gossip :refer :all]))

View file

@ -0,0 +1,134 @@
(ns the-great-game.gossip.news-items-test
(:require [clojure.test :refer :all]
[the-great-game.gossip.news-items :refer :all]))
(deftest location-test
(testing "Interest in locations"
(let [expected 1
actual (interest-in-location
{:knowledge [{:verb :steal
:actor :albert
:other :belinda
:object :foo
:location [{:x 35 :y 23} :auchencairn :galloway]}]}
:galloway)]
(is (= actual expected)))
(let [expected 2
actual (interest-in-location
{:knowledge [{:verb :steal
:actor :albert
:other :belinda
:object :foo
:location [{:x 35 :y 23} :auchencairn :galloway :scotland]}]}
[:galloway :scotland])]
(is (= actual expected)))
(let [expected 2
actual (interest-in-location
{:home [{:x 35 :y 23} :auchencairn :galloway :scotland]}
[:galloway :scotland])]
(is (= actual expected)))
(let [expected 0
actual (interest-in-location
{:knowledge [{:verb :steal
:actor :albert
:other :belinda
:object :foo
:location [{:x 35 :y 23} :auchencairn :galloway]}]}
[:dumfries])]
(is (= actual expected)))
(let [expected 7071.067811865475
actual (interest-in-location
{:home [{:x 35 :y 23}]}
[{:x 34 :y 24}])]
(is (= actual expected)
"TODO: 7071.067811865475 is actually a bad answer."))
(let [expected 0
actual (interest-in-location
{:home [{:x 35 :y 23}]}
[{:x 34 :y 24000}])]
(is (= actual expected)
"Too far apart (> 10000)."))
(let [expected true
actual (interesting-location?
{:knowledge [{:verb :steal
:actor :albert
:other :belinda
:object :foo
:location [{:x 35 :y 23} :auchencairn :galloway]}]}
:galloway)]
(is (= actual expected)))
(let [expected true
actual (interesting-location?
{:knowledge [{:verb :steal
:actor :albert
:other :belinda
:object :foo
:location [{:x 35 :y 23} :auchencairn :galloway]}]}
[:galloway :scotland])]
(is (= actual expected)))
(let [expected false
actual (interesting-location?
{:knowledge [{:verb :steal
:actor :albert
:other :belinda
:object :foo
:location [{:x 35 :y 23} :auchencairn :galloway]}]}
[:dumfries])]
(is (= actual expected)))
(let [expected true
actual (interesting-location?
{:home [{:x 35 :y 23}]}
[{:x 34 :y 24}])]
(is (= actual expected)))
(let [expected false
actual (interesting-location?
{:home [{:x 35 :y 23}]}
[{:x 34 :y 240000}])]
(is (= actual expected))))
(testing "Degrading locations"
(let [expected [:galloway]
actual (degrade-location
{:home [{0 0} :test-home :galloway]}
[{-4 55} :auchencairn :galloway])]
(is (= actual expected)))
(let [expected nil
actual (degrade-location
{:home [{0 0} :test-home :galloway]}
[:froboz])]
(is (= actual expected)))))
(deftest inference-tests
(testing "Ability to infer new knowledge from news items: single rule tests"
(let [expected {:verb :marry, :actor :belinda, :other :adam}
actual (infer {:verb :marry :actor :adam :other :belinda}
{:verb :marry :actor :other :other :actor})]
(is (= actual expected)))
(let [expected {:verb :attack, :actor :adam, :other :belinda}
actual (infer {:verb :rape :actor :adam :other :belinda}
{:verb :attack})]
(is (= actual expected)))
(let [expected {:verb :sex, :actor :belinda, :other :adam}
actual (infer {:verb :rape :actor :adam :other :belinda}
{:verb :sex :actor :other :other :actor})]
(is (= actual expected))))
(testing "Ability to infer new knowledge from news items: all applicable rules"
(let [expected #{{:verb :sex, :actor :belinda, :other :adam, :location nil, :nth-hand 1}
{:verb :sex, :actor :adam, :other :belinda, :location nil, :nth-hand 1}
{:verb :attack, :actor :adam, :other :belinda, :location nil, :nth-hand 1}}
;; dates will not be and cannot be expected to be equal
actual (make-all-inferences
{:verb :rape :actor :adam :other :belinda :location :test-home})
actual' (set (map #(dissoc % :time-stamp) actual))]
(is (= actual' expected)))))
(deftest learn-tests
(testing "Learning from an interesting news item."
(let [expected {:home [{0 0} :test-home],
:knowledge [{:verb :sex, :actor :adam, :other :belinda, :location nil, :nth-hand 1}
{:verb :sex, :actor :belinda, :other :adam, :location nil, :nth-hand 1}]}
actual (learn-news-item
{:home [{0, 0} :test-home] :knowledge []}
{:verb :sex :actor :adam :other :belinda :location [:test-home]})
actual' (assoc actual :knowledge (vec (map #(dissoc % :time-stamp) (:knowledge actual))))]
(is (= actual' expected)))))

View file

@ -79,8 +79,31 @@
(-> actual :cities :falkirk :stock :iron)
17)
"Stock should be topped up by the difference between the supply and
the demand amount."))
the demand amount."))))
)
)
(deftest run-test
(let [world (deep-merge
default-world
{:cities
{:aberdeen
{:stock {:fish 5}
:supplies {:fish 12}
:prices {:fish 1.1}}
:falkirk
{:stock {:iron 10}
:demands {:iron 5}
:supplies {:iron 12}
:prices {:iron 1.1}}}})
actual (run world)]
(is
(=
(-> actual :cities :aberdeen :stock :fish)
(+ (-> world :cities :aberdeen :supplies :fish)
(-> world :cities :aberdeen :stock :fish)))
"If stock is not empty and price is above cost, stock should be topped up by supply amount.")
(is
(=
(-> actual :cities :falkirk :stock :iron)
17)
"Stock should be topped up by the difference between the supply and
the demand amount.")))

View file

@ -22,3 +22,106 @@
expected 1.7] ;;
(is (= actual expected) "if information select the most recent")))))
(deftest burden-test
(testing "Burden of merchant"
(let [world (deep-merge
default-world
{:merchants
{:archie
{:stock
{:iron 1}}
:belinda
{:stock
{:fish 2}}
:callum
{:stock
{:iron 1
:fish 1}}}})]
(let [actual (burden :archie world)
expected (-> world :commodities :iron :weight)]
(is (= actual expected)))
(let [actual (burden :belinda world)
expected (* 2 (-> world :commodities :fish :weight))]
(is (= actual expected)))
(let [actual (burden :callum world)
expected (+
(-> world :commodities :iron :weight)
(-> world :commodities :fish :weight))]
(is (= actual expected)))
(let [actual (burden {} world)
expected 0]
(is (= actual expected)))
(let [actual (burden (-> world :merchants :deidre) world)
expected 0]
(is (= actual expected))))))
(deftest can-carry-test
(testing "What merchants can carry"
(let [world (deep-merge
default-world
{:merchants
{:archie
{:cash 5
:stock
{:iron 1}}
:belinda
{:stock
{:fish 2}}
:callum
{:stock
{:iron 1
:fish 1}}}})]
(let [actual (can-carry :archie world :fish)
expected 0]
(is (= actual expected)))
(let [actual (can-carry :belinda world :fish)
expected 8]
(is (= actual expected)))
(let [actual (can-carry (-> world :merchants :archie) world :fish)
expected 0]
(is (= actual expected)))
(let [actual (can-carry {:stock {:fish 7} :capacity 10} world :fish)
expected 3]
(is (= actual expected))))))
(deftest affordability-test
(testing "What merchants can afford to purchase"
(let [world (deep-merge
default-world
{:merchants
{:archie
{:cash 5
:stock
{:iron 1}}
:belinda
{:stock
{:fish 2}}
:callum
{:stock
{:iron 1
:fish 1}}}})]
(let [actual (can-afford :archie world :fish)
expected 5]
(is (= actual expected)))
(let [actual (can-afford :belinda world :fish)
expected 100]
(is (= actual expected)))
(let [actual (can-afford (-> world :merchants :archie) world :fish)
expected 5]
(is (= actual expected)))
(let [actual (can-afford {:cash 3 :location :buckie} world :fish)
expected 3]
(is (= actual expected)))
(is (thrown-with-msg?
Exception
#"No merchant?"
(can-afford :no-one world :fish)))
(is (thrown-with-msg?
Exception
#"No known location for merchant.*"
(can-afford {:cash 3} world :fish))))))
(deftest add-stock-test
(let [actual (add-stock {:iron 2 :fish 5} {:fish 3 :whisky 7})
expected {:iron 2 :fish 8 :whisky 7}]))

View file

@ -0,0 +1,79 @@
(ns the-great-game.time-test
(:require [clojure.test :refer :all]
;; [clojure.core.async :refer [thread <!]]
[the-great-game.time :refer :all]))
(deftest now-tests
(testing "Time progresses"
(let [t1 (now)]
(is (> t1 game-start-time))
(Thread/sleep 1000)
(is (> (now) t1)))))
(deftest game-time-tests
(testing "Getting game-time"
(is (= (game-time (inc game-start-time)) 1))))
(deftest calendar-tests
(testing "In-game calendar functions"
(let [expected :foot
actual (day 0)]
(is (= actual expected)))
(let [expected :stomach
actual (day (* 5 game-day-length))]
(is (= actual expected)))
(let [expected :foot
actual (day (* days-in-week game-day-length))]
(is (= actual expected)))
(let [expected :first ;; waiting day
actual (day (* 360 game-day-length))]
(is (= actual expected)))
(let [expected :first
actual (week 0)]
(is (= actual expected)))
(let [expected :second
actual (week (* days-in-week game-day-length))]
(is (= actual expected)))
(let [expected :first
actual (week (* days-in-season game-day-length))]
(is (= actual expected)))
(let [expected :foot
actual (season 0)]
(is (= actual expected)))
(let [expected :mouth
actual (season (* 180 game-day-length))]
(is (= actual expected)))
(let [expected :eye
actual (season (* 359 game-day-length))]
(is (= actual expected)))
(let [expected :waiting
actual (season (* 360 game-day-length))]
(is (= actual expected)))
(let [expected :foot
actual (season (* 365 game-day-length))]
(is (= actual expected)))))
(deftest date-string-tests
(testing "Date-string formatting"
(let [expected "First Foot of the Foot"
actual (date-string 0)]
(is (= actual expected)))
(let [expected "First Foot of the Nose"
actual (date-string
(* days-in-season game-day-length))]
(is (= actual expected)))
(let [expected "Third Mouth of the Mouth"
actual (date-string (* 180 game-day-length))]
(is (= actual expected)))
(let [expected "Fifth Plough of the Eye"
actual (date-string (* 359 game-day-length))]
(is (= actual expected)))
(let [expected "First waiting day"
actual (date-string (* 360 game-day-length))]
(is (= actual expected)))
(let [expected "First Foot of the Foot"
actual (date-string (* 365 game-day-length))]
(is (= actual expected)))))

View file

@ -0,0 +1,36 @@
(ns the-great-game.world.location-test
(:require [clojure.test :refer :all]
[the-great-game.world.location :refer :all]))
(deftest get-coords-test
(testing "Get coordinates of location"
(let [expected {:x 5 :y 7}
actual (get-coords {:x 5 :y 7})]
(is (= actual expected)))
(let [expected {:x -4 :y 55}
actual (get-coords [{:x -4 :y 55} :auchencairn :galloway :scotland])]
(is (= actual expected)))
(let [expected nil
actual (get-coords [:auchencairn :galloway :scotland])]
(is (= actual expected)))
))
(deftest distance-test
(testing "Distance between two locations"
(let [expected 4.242640687119285
actual (distance-between {:x 5 :y 5} {:x 2 :y 2})]
(is (= actual expected)))
(let [expected 3
actual (distance-between {:x 5 :y 5} {:x 2 :y 5})]
(is (= actual expected)))
(let [expected 50.80354318352215
actual (distance-between
{:x 5 :y 5}
[{:x -4 :y 55} :auchencairn :galloway :scotland])]
(is (= actual expected)))
(let [expected nil
actual (distance-between
{:x 5 :y 5}
[:auchencairn :galloway :scotland])]
(is (= actual expected)))
))