Merge branch 'develop'
This commit is contained in:
commit
dcf79bca7a
1
.calva/output-window/.clj-kondo/config.edn
Normal file
1
.calva/output-window/.clj-kondo/config.edn
Normal file
|
@ -0,0 +1 @@
|
|||
^:replace {:linters {}}
|
34
.gitignore
vendored
34
.gitignore
vendored
|
@ -18,11 +18,39 @@ pom.xml.asc
|
|||
.lein-failures
|
||||
.nrepl-port
|
||||
.cpcache/
|
||||
.calva/
|
||||
.idea/
|
||||
*~
|
||||
|
||||
|
||||
.calva/output-window/output.calva-repl
|
||||
.settings/
|
||||
|
||||
.classpath
|
||||
|
||||
.project
|
||||
|
||||
.lsp/sqlite.db
|
||||
|
||||
libbulletjme.so
|
||||
|
||||
liblwjgl64.so
|
||||
|
||||
libopenal64.so
|
||||
|
||||
.settings/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
.calva/
|
||||
.lsp/
|
||||
|
||||
|
||||
*.so
|
||||
|
||||
docs/cloverage/codecov.json
|
||||
|
||||
docs/cloverage/coverage.xml
|
||||
|
||||
src/cc/journeyman/the_great_game/cloverage.clj
|
||||
|
||||
.DS_Store
|
||||
|
||||
.portal/
|
||||
|
|
491
LICENSE
491
LICENSE
|
@ -1,214 +1,361 @@
|
|||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
|
||||
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
|
||||
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
### GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
1. DEFINITIONS
|
||||
Version 2, June 1991
|
||||
|
||||
"Contribution" means:
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
a) in the case of the initial Contributor, the initial code and
|
||||
documentation distributed under this Agreement, and
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
b) in the case of each subsequent Contributor:
|
||||
### Preamble
|
||||
|
||||
i) changes to the Program, and
|
||||
The licenses for most software are designed to take away your freedom
|
||||
to share and change it. By contrast, the GNU General Public License is
|
||||
intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
ii) additions to the Program;
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
where such changes and/or additions to the Program originate from and are
|
||||
distributed by that particular Contributor. A Contribution 'originates' from
|
||||
a Contributor if it was added to the Program by such Contributor itself or
|
||||
anyone acting on such Contributor's behalf. Contributions do not include
|
||||
additions to the Program which: (i) are separate modules of software
|
||||
distributed in conjunction with the Program under their own license
|
||||
agreement, and (ii) are not derivative works of the Program.
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if
|
||||
you distribute copies of the software, or if you modify it.
|
||||
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
"Licensed Patents" mean patent claims licensable by a Contributor which are
|
||||
necessarily infringed by the use or sale of its Contribution alone or when
|
||||
combined with the Program.
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this
|
||||
Agreement.
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on,
|
||||
we want its recipients to know that what they have is not the
|
||||
original, so that any problems introduced by others will not reflect
|
||||
on the original authors' reputations.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement,
|
||||
including all Contributors.
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at
|
||||
all.
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby grants
|
||||
Recipient a non-exclusive, worldwide, royalty-free copyright license to
|
||||
reproduce, prepare derivative works of, publicly display, publicly perform,
|
||||
distribute and sublicense the Contribution of such Contributor, if any, and
|
||||
such derivative works, in source code and object code form.
|
||||
### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby grants
|
||||
Recipient a non-exclusive, worldwide, royalty-free patent license under
|
||||
Licensed Patents to make, use, sell, offer to sell, import and otherwise
|
||||
transfer the Contribution of such Contributor, if any, in source code and
|
||||
object code form. This patent license shall apply to the combination of the
|
||||
Contribution and the Program if, at the time the Contribution is added by the
|
||||
Contributor, such addition of the Contribution causes such combination to be
|
||||
covered by the Licensed Patents. The patent license shall not apply to any
|
||||
other combinations which include the Contribution. No hardware per se is
|
||||
licensed hereunder.
|
||||
**0.** This License applies to any program or other work which
|
||||
contains a notice placed by the copyright holder saying it may be
|
||||
distributed under the terms of this General Public License. The
|
||||
"Program", below, refers to any such program or work, and a "work
|
||||
based on the Program" means either the Program or any derivative work
|
||||
under copyright law: that is to say, a work containing the Program or
|
||||
a portion of it, either verbatim or with modifications and/or
|
||||
translated into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".) Each licensee
|
||||
is addressed as "you".
|
||||
|
||||
c) Recipient understands that although each Contributor grants the licenses
|
||||
to its Contributions set forth herein, no assurances are provided by any
|
||||
Contributor that the Program does not infringe the patent or other
|
||||
intellectual property rights of any other entity. Each Contributor disclaims
|
||||
any liability to Recipient for claims brought by any other entity based on
|
||||
infringement of intellectual property rights or otherwise. As a condition to
|
||||
exercising the rights and licenses granted hereunder, each Recipient hereby
|
||||
assumes sole responsibility to secure any other intellectual property rights
|
||||
needed, if any. For example, if a third party patent license is required to
|
||||
allow Recipient to distribute the Program, it is Recipient's responsibility
|
||||
to acquire that license before distributing the Program.
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the Program
|
||||
(independent of having been made by running the Program). Whether that
|
||||
is true depends on what the Program does.
|
||||
|
||||
d) Each Contributor represents that to its knowledge it has sufficient
|
||||
copyright rights in its Contribution, if any, to grant the copyright license
|
||||
set forth in this Agreement.
|
||||
**1.** You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
3. REQUIREMENTS
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form under
|
||||
its own license agreement, provided that:
|
||||
**2.** You may modify your copy or copies of the Program or any
|
||||
portion of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
|
||||
b) its license agreement:
|
||||
**a)** You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
i) effectively disclaims on behalf of all Contributors all warranties and
|
||||
conditions, express and implied, including warranties or conditions of title
|
||||
and non-infringement, and implied warranties or conditions of merchantability
|
||||
and fitness for a particular purpose;
|
||||
|
||||
ii) effectively excludes on behalf of all Contributors all liability for
|
||||
damages, including direct, indirect, special, incidental and consequential
|
||||
damages, such as lost profits;
|
||||
**b)** You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any part
|
||||
thereof, to be licensed as a whole at no charge to all third parties
|
||||
under the terms of this License.
|
||||
|
||||
iii) states that any provisions which differ from this Agreement are offered
|
||||
by that Contributor alone and not by any other party; and
|
||||
|
||||
iv) states that source code for the Program is available from such
|
||||
Contributor, and informs licensees how to obtain it in a reasonable manner on
|
||||
or through a medium customarily used for software exchange.
|
||||
**c)** If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such interactive
|
||||
use in the most ordinary way, to print or display an announcement
|
||||
including an appropriate copyright notice and a notice that there is
|
||||
no warranty (or else, saying that you provide a warranty) and that
|
||||
users may redistribute the program under these conditions, and telling
|
||||
the user how to view a copy of this License. (Exception: if the
|
||||
Program itself is interactive but does not normally print such an
|
||||
announcement, your work based on the Program is not required to print
|
||||
an announcement.)
|
||||
|
||||
When the Program is made available in source code form:
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
Contributors may not remove or alter any copyright notices contained within
|
||||
the Program.
|
||||
**3.** You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
Each Contributor must identify itself as the originator of its Contribution,
|
||||
if any, in a manner that reasonably allows subsequent Recipients to identify
|
||||
the originator of the Contribution.
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
**a)** Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections 1
|
||||
and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities with
|
||||
respect to end users, business partners and the like. While this license is
|
||||
intended to facilitate the commercial use of the Program, the Contributor who
|
||||
includes the Program in a commercial product offering should do so in a
|
||||
manner which does not create potential liability for other Contributors.
|
||||
Therefore, if a Contributor includes the Program in a commercial product
|
||||
offering, such Contributor ("Commercial Contributor") hereby agrees to defend
|
||||
and indemnify every other Contributor ("Indemnified Contributor") against any
|
||||
losses, damages and costs (collectively "Losses") arising from claims,
|
||||
lawsuits and other legal actions brought by a third party against the
|
||||
Indemnified Contributor to the extent caused by the acts or omissions of such
|
||||
Commercial Contributor in connection with its distribution of the Program in
|
||||
a commercial product offering. The obligations in this section do not apply
|
||||
to any claims or Losses relating to any actual or alleged intellectual
|
||||
property infringement. In order to qualify, an Indemnified Contributor must:
|
||||
a) promptly notify the Commercial Contributor in writing of such claim, and
|
||||
b) allow the Commercial Contributor to control, and cooperate with the
|
||||
Commercial Contributor in, the defense and any related settlement
|
||||
negotiations. The Indemnified Contributor may participate in any such claim
|
||||
at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial product
|
||||
offering, Product X. That Contributor is then a Commercial Contributor. If
|
||||
that Commercial Contributor then makes performance claims, or offers
|
||||
warranties related to Product X, those performance claims and warranties are
|
||||
such Commercial Contributor's responsibility alone. Under this section, the
|
||||
Commercial Contributor would have to defend claims against the other
|
||||
Contributors related to those performance claims and warranties, and if a
|
||||
court requires any other Contributor to pay any damages as a result, the
|
||||
Commercial Contributor must pay those damages.
|
||||
**b)** Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your cost of
|
||||
physically performing source distribution, a complete machine-readable
|
||||
copy of the corresponding source code, to be distributed under the
|
||||
terms of Sections 1 and 2 above on a medium customarily used for
|
||||
software interchange; or,
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON
|
||||
AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
|
||||
EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR
|
||||
CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
|
||||
PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the
|
||||
appropriateness of using and distributing the Program and assumes all risks
|
||||
associated with its exercise of rights under this Agreement , including but
|
||||
not limited to the risks and costs of program errors, compliance with
|
||||
applicable laws, damage to or loss of data, programs or equipment, and
|
||||
unavailability or interruption of operations.
|
||||
**c)** Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is allowed
|
||||
only for noncommercial distribution and only if you received the
|
||||
program in object code or executable form with such an offer, in
|
||||
accord with Subsection b above.)
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
|
||||
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
|
||||
LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
|
||||
EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGES.
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
7. GENERAL
|
||||
**4.** You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt otherwise
|
||||
to copy, modify, sublicense or distribute the Program is void, and
|
||||
will automatically terminate your rights under this License. However,
|
||||
parties who have received copies, or rights, from you under this
|
||||
License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under
|
||||
applicable law, it shall not affect the validity or enforceability of the
|
||||
remainder of the terms of this Agreement, and without further action by the
|
||||
parties hereto, such provision shall be reformed to the minimum extent
|
||||
necessary to make such provision valid and enforceable.
|
||||
**5.** You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
|
||||
(excluding combinations of the Program with other software or hardware)
|
||||
infringes such Recipient's patent(s), then such Recipient's rights granted
|
||||
under Section 2(b) shall terminate as of the date such litigation is filed.
|
||||
**6.** Each time you redistribute the Program (or any work based on
|
||||
the Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails to
|
||||
comply with any of the material terms or conditions of this Agreement and
|
||||
does not cure such failure in a reasonable period of time after becoming
|
||||
aware of such noncompliance. If all Recipient's rights under this Agreement
|
||||
terminate, Recipient agrees to cease use and distribution of the Program as
|
||||
soon as reasonably practicable. However, Recipient's obligations under this
|
||||
Agreement and any licenses granted by Recipient relating to the Program shall
|
||||
continue and survive.
|
||||
**7.** If, as a consequence of a court judgment or allegation of
|
||||
patent infringement or for any other reason (not limited to patent
|
||||
issues), conditions are imposed on you (whether by court order,
|
||||
agreement or otherwise) that contradict the conditions of this
|
||||
License, they do not excuse you from the conditions of this License.
|
||||
If you cannot distribute so as to satisfy simultaneously your
|
||||
obligations under this License and any other pertinent obligations,
|
||||
then as a consequence you may not distribute the Program at all. For
|
||||
example, if a patent license would not permit royalty-free
|
||||
redistribution of the Program by all those who receive copies directly
|
||||
or indirectly through you, then the only way you could satisfy both it
|
||||
and this License would be to refrain entirely from distribution of the
|
||||
Program.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement, but in
|
||||
order to avoid inconsistency the Agreement is copyrighted and may only be
|
||||
modified in the following manner. The Agreement Steward reserves the right to
|
||||
publish new versions (including revisions) of this Agreement from time to
|
||||
time. No one other than the Agreement Steward has the right to modify this
|
||||
Agreement. The Eclipse Foundation is the initial Agreement Steward. The
|
||||
Eclipse Foundation may assign the responsibility to serve as the Agreement
|
||||
Steward to a suitable separate entity. Each new version of the Agreement will
|
||||
be given a distinguishing version number. The Program (including
|
||||
Contributions) may always be distributed subject to the version of the
|
||||
Agreement under which it was received. In addition, after a new version of
|
||||
the Agreement is published, Contributor may elect to distribute the Program
|
||||
(including its Contributions) under the new version. Except as expressly
|
||||
stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
|
||||
licenses to the intellectual property of any Contributor under this
|
||||
Agreement, whether expressly, by implication, estoppel or otherwise. All
|
||||
rights in the Program not expressly granted under this Agreement are
|
||||
reserved.
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the
|
||||
intellectual property laws of the United States of America. No party to this
|
||||
Agreement will bring a legal action under this Agreement more than one year
|
||||
after the cause of action arose. Each party waives its rights to a jury trial
|
||||
in any resulting litigation.
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
**8.** If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
**9.** The Free Software Foundation may publish revised and/or new
|
||||
versions of the General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Program does not specify a
|
||||
version number of this License, you may choose any version ever
|
||||
published by the Free Software Foundation.
|
||||
|
||||
**10.** If you wish to incorporate parts of the Program into other
|
||||
free programs whose distribution conditions are different, write to
|
||||
the author to ask for permission. For software which is copyrighted by
|
||||
the Free Software Foundation, write to the Free Software Foundation;
|
||||
we sometimes make exceptions for this. Our decision will be guided by
|
||||
the two goals of preserving the free status of all derivatives of our
|
||||
free software and of promoting the sharing and reuse of software
|
||||
generally.
|
||||
|
||||
**NO WARRANTY**
|
||||
|
||||
**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
### END OF TERMS AND CONDITIONS
|
||||
|
||||
### How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these
|
||||
terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to
|
||||
attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
one line to give the program's name and an idea of what it does.
|
||||
Copyright (C) yyyy name of author
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper
|
||||
mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
|
||||
type `show w'. This is free software, and you are welcome
|
||||
to redistribute it under certain conditions; type `show c'
|
||||
for details.
|
||||
|
||||
The hypothetical commands \`show w' and \`show c' should show the
|
||||
appropriate parts of the General Public License. Of course, the
|
||||
commands you use may be called something other than \`show w' and
|
||||
\`show c'; they could even be mouse-clicks or menu items--whatever
|
||||
suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or
|
||||
your school, if any, to sign a "copyright disclaimer" for the program,
|
||||
if necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright
|
||||
interest in the program `Gnomovision'
|
||||
(which makes passes at compilers) written
|
||||
by James Hacker.
|
||||
|
||||
signature of Ty Coon, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library,
|
||||
you may consider it more useful to permit linking proprietary
|
||||
applications with the library. If this is what you want to do, use the
|
||||
[GNU Lesser General Public
|
||||
License](https://www.gnu.org/licenses/lgpl.html) instead of this
|
||||
License.
|
||||
|
|
|
@ -3,11 +3,9 @@ Prototype code towards the great game I've been writing about for ten years, and
|
|||
|
||||
## Awful warning
|
||||
|
||||
This doesn't work and isn't ever likely to fully work: it's way too ambitious for any
|
||||
single person to actually build. Feel free to mine it for algorithms and ideas, but
|
||||
don't expect a game you can actually play any time soon.
|
||||
This doesn't work and isn't ever likely to fully work: it's way too ambitious for any single person to actually build. Feel free to mine it for algorithms and ideas, but don't expect a game you can actually play any time soon.
|
||||
|
||||
## There is documentation
|
||||
## There is (masses of) documentation
|
||||
|
||||
[here](https://simon-brooke.github.io/the-great-game/)
|
||||
|
||||
|
|
86
doc/A-generic-planning-algorithm-for-craftworker-npcs.md
Normal file
86
doc/A-generic-planning-algorithm-for-craftworker-npcs.md
Normal file
|
@ -0,0 +1,86 @@
|
|||
# A Generic Planning Algorithm for craftworker NPCs
|
||||
|
||||
## Preamble
|
||||
|
||||
The Great Game requires a number of different crafts to be performed, both because the economy depends on the products of those crafts and to provide verisimilitude and set dressing. Some of those crafts, the relations between them, and the progression within them are set out in [Populating a game world](Populating-a-game-world.html).
|
||||
|
||||
For the purposes of planning work, only Master craftspeople are considered.
|
||||
|
||||
A Master craftsperson has
|
||||
|
||||
1. a house and appropriate workshop, within a settlement;
|
||||
2. zero or more apprentices;
|
||||
3. zero or more journeyman;
|
||||
4. a spouse, who is usually of lower status;
|
||||
5. zero of more coresident children;
|
||||
6. zero or more coresident non-working parents/elders.
|
||||
|
||||
There are limits to the number of apprentices and journeymen a master may take on, essentially based on demand in the local market. The master is responsible for housing and feeding all of the household including apprentices and journeymen, and for obtaining sufficient craft supplies. All craft work done in the household belongs to the master.
|
||||
|
||||
Apprentices are definitely not paid. Journeymen should be paid, but this is a detail to ignore until we have other things working.
|
||||
|
||||
Journeymen will move on from master to master from time to time — infrequently, but it will happen; and may be dismissed by masters when markets are tight. Journeymen probably learn their craft recipes — which is to say, the items and qualities of item they are able to craft — from the masters they work with. Consequently, journeymen will seek out masters with higher reputation; masters will prefer journeymen with more experience.
|
||||
|
||||
Apprentices do not move on until the end of their period of apprenticeship (16th birthday?) when they become journeymen.
|
||||
|
||||
The master will plan work in four hour sessions - essentially, a morning session and an afternoon session each day.
|
||||
|
||||
All craftspeople have regular schedules covering mealtimes, sleep, and festivals. A lower status person within the household will have regular schedules covering each of fetching water, fetching fuel wood, taking out night soil, feeding chickens, washing dishes and laundry, and so on.
|
||||
|
||||
When the master works in the workshop, all the apprentices and journeymen will also work in the workshop; when the master is engaging in recreation, they're also engaging in recreation. What they do when the master is e.g. going to market, I haven't yet decided.
|
||||
|
||||
## Commodity items and special commissions
|
||||
|
||||
In principle all craftspeople may make both commodity items and special commission items, but in practice many crafts will be mostly commodity and a few will be almost entirely special commission (for example a diplomat doesn't produce peace treaties prèt-à-porter); but I don't yet have a good model of how I'm going to handle special commissions, so I'm just doing some hand waving here to say they will exist and must be handled.
|
||||
|
||||
## The algorithm
|
||||
|
||||
A master craftsperson needs to keep stock of a number of things
|
||||
|
||||
1. Sufficient food for the household;
|
||||
2. Sufficient craft materials for immediate production;
|
||||
3. Sufficient funds to buy more food/craft materials when needed;
|
||||
4. Commodity craft items produced;
|
||||
5. Craft items work in progress.
|
||||
|
||||
### Choosing tasks
|
||||
|
||||
So in planning a period of work, the master has to decide:
|
||||
|
||||
1. Do I need to go to market?
|
||||
1. Is there news of a travelling merchant who buys what I produce arriving at my nearest market? -> go to market;
|
||||
2. Is the household running low on food? -> go to market;
|
||||
3. Is the household running low on craft materials? -> go to market;
|
||||
2. Do I have any commissioned items to produce? -> produce commissioned items;
|
||||
3. Should I work on commodities or take the day off?
|
||||
This is a throw-of-the-dice decision, influenced by
|
||||
1. Cash on hand (if there's little, greater incentive to work);
|
||||
2. Weather (if it's especially good, less incentive to work);
|
||||
3. Gossip (if there's interesting news, less incentive to work)
|
||||
|
||||
### Commodity production
|
||||
|
||||
If the decision is to work on commodities, the next decision is what commodity item to produce.
|
||||
|
||||
For each craft recipe the master knows there will be
|
||||
|
||||
1. A list of quantities of different craft materials needed per item, for example a sword might need two kilograms of steel of a particular quality, ten kilograms of charcoal, one kilogram of timber, half a square metre of leather;
|
||||
2. An amount of craftsperson time - for example, a standard infantry sword might take ten hours;
|
||||
3. Memory of prices achieved by item to that recipe in the local market.
|
||||
|
||||
The master will choose a recipe for which there are sufficient materials on hand, and which is profitable to make — the more profitable, the more likely to be selected (but I think there's probably some furtive dice rolling under the table here too; you don't want all the smiths in town producing infantry swords at the same time, because that would swamp the market and drive prices down).
|
||||
|
||||
When an item is started, the materials for it are removed from stock and assigned to the item, which is added to the work in progress list. The number of items that can be produced in a work session is
|
||||
|
||||
```clojure
|
||||
(/ (* hours-in-session people-in-team)
|
||||
hours-to-produce-one-item)
|
||||
```
|
||||
|
||||
At the end of the session, the integer number of items produced is removed from the work in progress queue and added to stock, and the modulus is added as `:work-done` to the remaining item, which is left in the work in progress queue.
|
||||
|
||||
Obviously items in the work in progress queue may need to be completed at the start of the next commodity work session.
|
||||
|
||||
Obviously, none planned at sufficient granularity to be animated unless the workplace is in the `:active` circle, and none of it gets actually animated unless it's actually on camera, but the book-keeping in terms of food and craft materials consumed and of items produced must be done.
|
||||
|
||||
This implies that at least many master craftspeople must be in the `:background` circle, i.e. woken up once every game day to plan a work session, no matter how far away the player character is.
|
33
doc/API_Spec.md
Normal file
33
doc/API_Spec.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# API Spec (unfinished)
|
||||
|
||||
If the Gossip system is ever to be deployed in practice at all, it will need to be deployed as a library add-on to someone else's game, since in practice The Great Game will never be even nearly finished. The game engine already knows many of the things the Gossip system needs to know; that we need to define is an interface which allows Gossip, considered as a subsystem, to query the game engine.
|
||||
|
||||
My preference is still that Gossip should be written in a Lisp-like language - and, for now, in Clojure - simply because that is most comfortable to me. It needs bidirectional socket communication with the game engine, over which it sends either [extensible data notation](https://github.com/edn-format/edn) or [JavaScript Object Notation](https://www.json.org/json-en.html), with a preference for the former.
|
||||
|
||||
## Tracking what happens in the world
|
||||
|
||||
Existing game engines don't tend to track in convenient form things which have happened off-camera - indeed, mostly, things don't happen at all when the player isn't present. They don't even track much that happens when the player is present, and they usually track what they do track in fairly ad-hoc ways. So generally Gossip-as-library will have to maintain its own history of what has happened, and who knows what about what has happened; and will have to model the major life events of non-player characters happening off-camera (if this is done at all) itself.
|
||||
|
||||
## Interrogating lore
|
||||
|
||||
Many games have a great deal of lore and many lore texts. It's reasonable to expect each non-player character to know a certain amount of lore, certainly lore which is local to their home location, or relevant to their trade. In order to make that available to Gossip, you probably need to construct a searchable corpus of all the lore, which can be simply queried.
|
||||
|
||||
That obviously then needs to be filtered by what the respondent can be expected to know, but that's a problem Gossip has to handle anyway.
|
||||
|
||||
## Interrogating the map
|
||||
|
||||
### get-character-location *id*
|
||||
|
||||
Returns the player location in the world of the character with the specified id, as at minimum a three dimensional coordinate tuple, with heading; optionally with hierarchical region ids.
|
||||
|
||||
### get-potential-auditors *id*
|
||||
|
||||
### get-potential-auditors *id*, *volume*
|
||||
|
||||
Return an ordered list of ids of characters spatially close to the character with the specified id, ordered by their likelihood of being the character addressed (i.e. preferring characters in front of the character with the specified id to those off to the side or behind, on a sort of cardioid pattern). The set is bounded by the distance at which speech is deemed to be intelligible, which may be a constant, or maybe modified by some modelling of ambient noise, or the volume of the character's speech act.
|
||||
|
||||
### get-potentially-aware *id*
|
||||
|
||||
### get-potentially-aware *id*, *volume*
|
||||
|
||||
As above, but return a list of ids of characters within a distance in which speech may be heard but not intelligibly.
|
33
doc/Appraisal.md
Normal file
33
doc/Appraisal.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Appraisal (unfinished)
|
||||
|
||||
## What is Appraisal
|
||||
|
||||
There's an thing that all non player characters can do, which varies greatly from person to person, and which is of particular importance to merchants, and that is appraisal.
|
||||
|
||||
Each category of goods has different dimensions of quality. A sword may be evaluated, for example, on
|
||||
|
||||
| Dimension | Better is | Ease of appraisal |
|
||||
| -------------------- | ---------------------------------- | ----------------- |
|
||||
| Hardness | More | Difficult |
|
||||
| Toughness | More | Difficult |
|
||||
| Wear | Less | Intermediate |
|
||||
| Weight | There's a sweet spot, but it's low | Easy |
|
||||
| Length | Judgement call | Easy |
|
||||
| Decoration/Showiness | Judgement call | Intermediate |
|
||||
| Workmanship | Better | Intermediate |
|
||||
|
||||
A person learns to appraise the qualities of a sword by having direct experience of swords with a range of values for the particular quality. A person who's only ever handled one sword does not know whether that sword heavy or light, pristine or worn. However, once a person has handled a dozen swords of different weights, they'll have some idea of what weight an average sword is, even if their idea may actually be a little off. Weight and length are easy to assess.
|
||||
|
||||
Similarly, once someone has handled a few dozen swords with different degrees of wear, will have an idea of how many chips, how much corrosion or pitting, is normal. Wear is harder to assess, but it doesn't need particular techniques or skills to assess, just observation. To assess hardness, you really need to have sharpened the blade and then used it to the extent that it needs sharpening again, but if you've handled a lot of blades of varying qualities you may associate patterns in the steel, such as pattern welding, damascus steel, or a hamun, or particular markers' marks, with varying hardnesses. Toughness is even harder to assess (without actually chipping or breaking the blade) and is really going to come down to recognising either high quality steels or particular makers' marks.
|
||||
|
||||
## Developing appraisal skill
|
||||
|
||||
So: how does one gain experience? I'm going to assume that anyone who's bought a sword has handled it before making the choice. That anyone who's survived on the winning side f a battle unwounded will also have handled eight of each type of weapon used, for each such battle (the victors will have the pick of the spoils on the battlefield). That a weapon smith has handled sixteen for each year they've been working. And possibly that a master weapon smith will at least examine more weapons in a year than a journeyman, who will at least examing more than an apprentice. But, essentially, appraisal skill develops with exposure to items in the particular category. The exact mechanism for tracking this I'm unsure of, because there is a tradeoff between richness of records and data compactness, and this game looks like getting rather big.
|
||||
|
||||
Of course, some people may be more observant than others, so it's possible that some people may gain appraisal skill on the basis of less exposure than others. But at this moment that's not a thing I'm planning to model.
|
||||
|
||||
## What does appraisal skill buy you?
|
||||
|
||||
In any category of goods, some individual items are better than others, and this difference may be significant. A person with good appraisal skill will recognise this difference. So a person with good skills, offered two items at the same price, will be able to select the better one; if bargaining for an item, will be prepared to offer a higher price for the better one; if selling items, will be prepared to sell the better one only for a higher price.
|
||||
|
||||
Price arbitrage is how a static merchant makes money.
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
#### Wednesday, 8 May 2019
|
||||
|
||||

|
||||

|
||||
|
||||
*Devorgilla's Bridge in Dumfries, early fourteenth century. This clearly shows how a genetic buildings approach to bridges can be made to work: a single element is repeated to span the necessary distance. That element can be stretched vertically and laterally to match the location, and can be rendered in different stone finishes to match local geology.*
|
||||
|
||||
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.
|
||||
|
||||
|
@ -72,7 +74,7 @@
|
|||
|
||||
## 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.
|
||||
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-world.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.
|
||||
|
||||
|
|
130
doc/Biomes_and_ecology.md
Normal file
130
doc/Biomes_and_ecology.md
Normal file
|
@ -0,0 +1,130 @@
|
|||
# Biomes and ecology (unfinished)
|
||||
*The motivation for this document was to explain the mulberry trees in the Tcha valley, and think about why Tchahua is especially a centre for the silk trade*
|
||||
|
||||
## Broader geography
|
||||
|
||||
The broader geography of the world is not a matter for this document, but TODO: there isn't yet a document which usefully describes it, and there needs to be.
|
||||
|
||||
|
||||
## Biomes relevant at this stage
|
||||
|
||||
### 1. Steppe
|
||||
|
||||
The centre of the continent is the steppe; it is generally too arid for forest growth, and is therefore scrub and grassland. There is one principal river system, which feeds into a marshland in the south, from which the water then goes underground beneath the limestone plateau to become the Tcha and Sind rivers. In late summer there's little water in the river, and few other waterholes. Antelope, camels, horses, goats, possibly sheep are native to the steppe, and there are probably something like leopards which predate on them, but I haven't fleshed it out.
|
||||
|
||||
Big dragons don't hunt on the steppe because they can't take off from flat ground, but smaller dragons may do so.
|
||||
|
||||
Settled by the steppe tribes, who are nomadic herders, extremely warlike but not technically highly developed. They are the game world's principle horse breeders. Basically in the game as I'm working on it at present, the player cannot go north across the steppe because the steppe tribes are too hostile.
|
||||
|
||||
A single major road, the Caravan Road, runs north to south across the steppe. There were in the past fortified caravanserrais along the length of the road, established and protected by Hans'hua, but they have been progressively overrun and destroyed by the steppe tribes and are now ruinous. Only one remains: the North Inn, just below the northern slope of the plateau. There is some limited horticulture on land close to the South Inn, supplying markets in Hans'hua.
|
||||
|
||||
### 2. Plateau
|
||||
|
||||
The limestone plateau runs along the whole of the southern edge of the steppe, from the western massif to the rim of the crater which forms The Great Place. It is a landscape of clints and grykes, on which nothing grows, and on which there is no water. It is about four day's journey by fast horse from north to south. The caravan road crosses the plateau from the North Inn to another caravanserrai, the South Inn, located in the north end of the Tcha valley. Because of the dense chaotic pattern of clints and grykes and the lack of accessible water, it is effectively impossible to cross the plateau other than by the caravan road, or by another path to the extreme east of the plateau, where it abuts the mountains of the Rim.
|
||||
|
||||
#### 2.1 Hans'hua
|
||||
|
||||
There is one city, Hans'hua, on the caravan road about half way across the plateau, where wind-pumps lift water from the underground river. Apart from this one city, nothing lives on the plateau. Migrating birds cross it, and that is all.
|
||||
|
||||
The city is small, walled, and run as an extreme neoliberal oligarchy; the city's main industry is maintaining the wind pumps, and its entire income is from tolls on caravans passing along the caravan road. This has been, and is still, extremely lucrative, but it is obvious both to the long distance merchants and to the oligarchs that the new ships are going to make the caravans too slow, too risky and too expensive to compete, and that as more ships are built, traffic on the caravan road will dwindle.
|
||||
|
||||
### 3. Massif
|
||||
|
||||
There's a granite intrusion which forms the entire western coast of the continent. It's geologically old and consequently the hills, though high, are rounded rather than jagged; at the southern end of the range (which is the only part that's in the least fleshed out yet) they're not snow covered in summer. As the prevailing winds are westerly, this massif intercepts most of the rain, which is why the steppe is arid.
|
||||
|
||||
Consequently it's pretty thoroughly forested, and the southern parts of it are mainly broadleaf forests including high quality hardwoods and many fruiting trees. Understory typical of mediterranean littoral forests, about which I don't really know enough.
|
||||
|
||||
Deer, cattle, pigs, wolves, leopards, badgers, squirrels… masses of birds of all appropriate types.
|
||||
|
||||
Because it's an old granite intrusion, the soil in the valleys is largely clay. There are mineral rich veins with a considerable range of minerals, but, obviously, not all in the same place.
|
||||
|
||||
Settled by the Western Clans, a negroid people living mainly in small isolated villages in the forest, with mostly limited agriculture.
|
||||
|
||||
#### 3.1 Northern massif
|
||||
|
||||
I haven't yet fleshed this out, but there are probably permanent snows and the forests are probably coniferous. It's my current working assumption that the new great ships are built from old growth conifers, taken from forests in the northern massif; but as I say this is not yet fleshed out.
|
||||
|
||||
The northern culture have developed very high quality ceramics using clay from the massif, including stonewares and porcelaines. I think the same clays also exist in the south of the massif, but the technology for producing high quality ceramics does not exist there. The northerners are also making high quality steels from magnetite and haematite from the massif. Whether these ores exist in the south I don't yet know.
|
||||
|
||||
#### 3.2 Dor
|
||||
|
||||
The northernmost of the western clans, the Dor, live in the central massif north of Andale, but apart from the fact that they exist and they're there, I don't yet really know much.
|
||||
|
||||
#### 3.3 Andale
|
||||
|
||||
The river An rises in the east of the massif near the south-west corner of the steppe, west of where the village and market of Dawnhold now stand, and flows more or less due westward. There are two major drops in the river's course, the upper a day's travel east of Silverhold, which is an actual fall of at least six metres, and the lower at Anghold. Between Anghold and Silverhold the river is navigable by small shallow draught boats; west of Anghold it is navigable down to the sea at Anmouth.
|
||||
|
||||
There are freshwater and migratory fish in the river, and fishing is a source of protein and livelihood the whole length of the valley.
|
||||
|
||||
The valley is largely forested. Apart from wild animals, domestic cattle and pigs are herded in the forest. Trees include alder, almond, apple, apricot, ash, beech, birch, cherry, chestnut, hazel, holly, lime, maple, mulberry, oak, orange, pear, walnut, yew.
|
||||
|
||||
##### 3.3.1 Dawnhold
|
||||
|
||||
Dawnhold isn't strictly geographically in Andale — it's east of, on the steppe side of, the watershead, but it marks the eastern border of the lands settled by clan An. There's an annual market, a village, and a garrisoned fortification to deter raids by the steppe tribes.
|
||||
|
||||
##### 3.3.2 Silverhold
|
||||
|
||||
Small town serving the only significant silver mine in the world. A tributary flows in from the north here, but I know nothing about it yet. There is a major fortification/refinery/treasury. All around Silverhold, right up to the headwaters of the An and right down to Anghold, the valley is forested with only small clearings round villages, which are mainly close to the river.
|
||||
|
||||
Many other metals — certainly inluding lead, tin, and small quantities of gold, probably not copper — come out of the mines at Silverhold.
|
||||
|
||||
The An produce enough ferrous metals for their own tools and weapons, but their iron smelting technology is not advanced and they don't export iron or iron products. They produce eathernware ceramics for domestic consumption. They produce leather and linen, and textiles from nettle fibres. They produce timber, which is their principal building material, but they don't export it. In practice their major export is silver coinage.
|
||||
|
||||
##### 3.3.3 Longwater
|
||||
|
||||
Longwater is a long, narrow lake, like Loch Lomond, on a tributary which flows into the An from the south, joining upstream from Anghold. There is no major nucleated settlement on Longwater, but there are sufficient small villages and hamlets on its banks to form an identifiable settlement cluster. Small boats can make it downstream from Longwater from the An and back, probably with some degree of portage around rapids. There's a pass over from the south of the Longwater valley to Gor territory, but it's high, difficult, and not much used. The whole of the Longwater valley is broadleaf forest.
|
||||
|
||||
##### 3.3.4 Anghold
|
||||
|
||||
There's a small town, market and fortification — Anghold — on the south bank of the An, just above first cataract, where boats are portaged up from the lower river to the middle reach. Downstream from Anghold the river is wider, slower and more meandering, with marshy banks. The valley west of Anghold, especially on the southern side, is also more populated, with more of the forest cleared and more arable agriculture.
|
||||
|
||||
##### 3.3.5 Anmouth
|
||||
|
||||
The An meets the sea at Anmouth, where there is a deep harbour at a bend in the river just east of a long estuary, There is a bar, making it dangerous to enter the harbour in bad weather, and the whole estuary is pretty exposed to western storms, although there may be some islands providing some shelter for emergency anchorages — I don't have that level of detail yet. Certainly the big new ships do not yet call in here, but could.
|
||||
|
||||
There are farming and sea-fishing villages down both sides of the estuary. There is no tradition of ship building, however.
|
||||
|
||||
#### 3.4 Gor
|
||||
|
||||
Clan Gor occupy the south-western peninsula of the continent, and the south slope of the massif, east almost as far as the Tcha valley. Their land is forested with a similar mix of trees to Andale. They have no major rivers, but several minor ones. They live mainly in coastal villages, and sea fishing is a major economic activity. They have no deep water ports.
|
||||
|
||||
In addition they do mine iron, and they have exported swords, but the market for their swords is being undercut by better crucible steel swords from the north now being imported into the Cities of the Coast by the new ships. Similarly, the Gor used to export earthenware, but that too is now being undercut by stonewares and porcelaines from the north.
|
||||
|
||||
Because of a history of being victims of raiding from the Cities of the Coast, the Gor maintain a fortified eastern border along the line of hills to the west of the Tcha valley. Nevertheless they have mostly good trading relationships with Tchahua. In particular they export large quantities of raw and spun silk, and some woven silk cloth, to Tchahua.
|
||||
|
||||
I don't yet have nearly a clear enough picture of the organisation and layout of the Gor lands, but their major stronghold and administrative centre must be to the east. While they traditionally had the communist and democratic culture of the other Western Clans, one family have become dominant and have become effectively hereditary leaders, influenced by the cultures to their east. However the leading family do not self-identify as aristons.
|
||||
|
||||
### 4. Coast
|
||||
|
||||
"The Coast" is the name given to the southern littoral of the continent, west of the Great Place and east of the Massif. It's limestone, with deeply cut, steep sided valleys separated by high, arid uplands, with scrubby vegetation, grazed by domestic sheep and both domestic and wild goats.
|
||||
|
||||
The native culture were peaceable, communistic agriculturalists, not greatly different from the Western Clans. However some several hundred years ago they were invaded by a warrior group from the steppe tribes, who established themselves as a military aristocracy — the Aristae — and started to build cities — and impose taxes onto the peasantry, forcing them into a more or less cash oriented economy.
|
||||
|
||||
The valleys were once forested, but the central valleys, which were in any case rather dryer and where the Aristae first settled and established cities, are now mostly cleared, and are a mix of pastoral and arable, with considerable viticulture.
|
||||
|
||||
#### 4.1 Tcha valley
|
||||
|
||||
The Tcha is the westernmost — and largest — river of the coast. It emerges from under the plateau at a pool under the South Tower marking the southern limit of Hans'hua territory and runs south more or less along the divide between the granite to the west and the limestone to the east. It is still largely forested, partly because it is relatively recently occupied by the Aristae, but partly because of the growth of the silk industry. This has led to some forested areas, especially near the navigable reaches of the river, being converted into mulberry orchards. However, there's still a great deal of mixed forest, and the majority of mulberry leaves for feeding to silk worms are gathered from natural forest.
|
||||
|
||||
A road branches off from the main Caravan Road at the South Inn and runs down the eastern side of the valley, to a ferry across the Sind river, where it joins the Tcha as a tributary, at the village helpfully known as Sind Ferry, and thence to Tchahua.
|
||||
|
||||
Mulberries, by-products of the silk industry, are used in the production of brandy. Mulberry wine is produced in villages in the forest, and transported down river to a distillery at Sind Ferry, where it is distilled. Some mulberry wine may be sold in Tchahua for drinking as wine (and it is certainly drunk in the villages), but it is generally considered inferior to grape wine.
|
||||
|
||||
There is some arable and mixed agriculture, mainly towards the southern end of the valley on the western (less steep) side, although this side is also largely forested.
|
||||
|
||||
##### 4.1.1 Tchahua
|
||||
|
||||
The city of Tchahua lies on the east bank of the river at the head of its estuary, and has deep water — the only really usable deep water port on the coast, being not only the largest river but also the least silted. Until quite recently it had been a small provincial silk weaving city, nominally independent but in fact paying tribute to both Sinhua to its east and the Gor to its west, in order to avoid being formally conquered by either.
|
||||
|
||||
There's a multi-span bridge here — I think a pontoon bridge — of which the eastern most span is a drawbridge which can be lifted into a fortified gateway on the eastern (Tchahua) shore. There is a fishing industry, but as the eastern side
|
||||
|
||||
Industries are silk weaving and dying, and fishing. Very recently, a new deep water quay has been constructed and the first large ships have begun to call. It is obvious that the city is going to become much more important as a strategic market and transport hub, but that has only just begun to have effect.
|
||||
|
||||
### 4.2 Sind valley
|
||||
|
||||
#### 4.2.1 Sinhua
|
||||
|
||||
|
||||
|
||||
|
||||
|
9
doc/Building_on_microworld.md
Normal file
9
doc/Building_on_microworld.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Building on Microworld
|
||||
|
||||
In [Settling a Game World](Settling-a-game-world.html) I intended that a world should be populated by setting agents - settlers - to explore the map and select places to settle according to particular rules. In the meantime, I've built [MicroWorld](https://github.com/simon-brooke/mw-ui), a rule driven cellular automaton which makes a reasonably good job of modelling human settlement. It works, and I now plan to use it, as detailed in this note; but there are issues.
|
||||
|
||||
First and foremost, it's slow, and both processor and memory hungry. That means that at continent scale, a cell of one kilometre square is the minimum size which is really possible, which isn't small enough to create a settlement map of the density that a game will need. Even with 1 km cells, even on the most powerful machines I have access to, a continent-size map will take many days to run.
|
||||
|
||||
Of course it would be possible to do a run at one km scale top identify areas which would support settlement, and then to do a run on a ten metre grid on each of those areas to more precisely plot settlement. That's an idea which I haven't yet explored, which might prove fruitful.
|
||||
|
||||
Secondly, being a cellular automaton, MicroWorld works on a grid. This means that everything is grid aligned, which is absolutely not what I want! So I think the way to leverage this is to use MicroWorld to establish which kilometre square cells om the grid should be populated (and roughly with what), and then switch to *ad hoc* code to populate those cells.
|
55
doc/Canonical-dictionary.md
Normal file
55
doc/Canonical-dictionary.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Canonical dictionary for this documentation
|
||||
|
||||
Where a word is used in the documentation for The Great Game and its related projects, this file describes the canonical meaning of that word. This is because a lot of the concepts in play are messy and ambiguous, so that at times even I am confused by what I mean. The presence of this file is an acknowledment of this difficulty, and an implicit admission that not all the documentation is, at this stage anyway, consistent.
|
||||
|
||||
#### Actor
|
||||
|
||||
An `actor` is a thing which performs actions within the game world. Thus a tree is (almost certainly) not an actor, and things like sheep and rabbits that run about are probably not actors, but an animal which may pro-actively interact with the player character (such as a predator, or a beast of burden, or even a prey species which may flee) is an actor. In [god mode](#God_mode), if implemented, the player can inhabit any actor within the game world.
|
||||
|
||||
#### Agent
|
||||
|
||||
`Agent` is probably just a synonym for `actor`. If it is different in any way, that way has not yet been determined.
|
||||
|
||||
#### Gossip
|
||||
|
||||
A `gossip` is an `actor` who exchanges news with other `actors`, even when the player character is not nearby. Thus `gossips` are the mechanism by which news propagates through the game world, and also the mechanism by which information degrades. Broadly:
|
||||
|
||||
1. `innkeepers` (and possibly some others) are `gossips` who do not move; rather, they gather information from gossips who do move, and all `non-player characters` local to the are deemed to know everything that their local `innkeeper` knows;
|
||||
2. `merchants` (and possibly some others) are `gossips` who do move from place to place, and thus transfer news.
|
||||
|
||||
See [the spread of knowledge in a large game world](The-spread-of-knowledge-in-large-game.html).
|
||||
|
||||
#### Heightmap
|
||||
|
||||
A `heightmap` is a raster image of the world, such that the intensity in which an area is coloured represents the value of some variable, by default height, of that area.
|
||||
|
||||
#### Holding
|
||||
|
||||
A `holding` is a polygon 'owned' by an `actor` on which are built appropriate building units representing the `actors` craft and status.
|
||||
|
||||
#### Location
|
||||
|
||||
A `location` value is a sequence comprising at most the x/y coordinate location and the ids of the settlement and region (possibly hierarchically) that contain the location. If the x/y is not local to the home of the receiving agent, they won't remember it and won't pass it on; if any of the ids are not interesting, they won't be passed on. 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.
|
||||
|
||||
**Examples**
|
||||
|
||||
1. \[{:x 5445678 :y 9684351}\]
|
||||
2. \[{:x 5445678 :y 9684351} :karalin-palace :hanshua\]
|
||||
|
||||
#### Merchant
|
||||
|
||||
A `merchant` is an `actor` and `gossip` who trades goods, and incidentally conveys news, between `markets`.
|
||||
|
||||
#### Non-player character
|
||||
|
||||
A `non-player character` is, for our purposes, an `actor` capable of engaging in conversation with the `player character`. Note, however, that, from a software point of view, the `player character` is just a special case of a `non-player character`.
|
||||
|
||||
#### Player character
|
||||
|
||||
The `player character` is the unique `actor` within the game currently controlled and inhabited by the player.
|
||||
|
||||
#### Route
|
||||
|
||||
A `route` is a pre-prepared path through the game world that an `actor` may take. Most `actors` are not constrained to follow `routes`, but in general `routes` have lower traversal cost than other terrain.
|
27
doc/Division_of_tasks_between_server_and_client.md
Normal file
27
doc/Division_of_tasks_between_server_and_client.md
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Division of tasks between server and client
|
||||
|
||||
An alternative nomentclature I may use for this dichotomy would be _planner_ and _performer_; it would be the same dichotomy. 'Planner' and 'server' are synonyms; 'performer' and 'client' are synonyms.
|
||||
|
||||
## What do I mean by the 'server'?
|
||||
|
||||
There is something which manages game state and things like the gossip network, merchant network, and major world events. This something is almost certainly written in some form of Lisp; I'd prefer Clojure but I don't think it's performant enough so probably Common Lisp. This means that it has inevitable pauses for garbage collection. Underneath this is a database which handles persistent storage of game state, which is probably an SQL database and quite likely [SQLite](https://www.sqlite.org/index.html).
|
||||
|
||||
The initial idea of The Great Game is that it is a single player game, but it actually doesn't need to be and it would be quite possible for one server to support multiple clients, each being used by a different player.
|
||||
|
||||
The server/planner decides what each actor does, models what each character knows, plans and records all actions and transactions. It plans speech acts, and handles conversations which happen off screen, but hands speech texts over to the client/performer layer for actual performance. It also plans journeys as described in [Pathmaking](Pathmaking.html), but it doesn't deal with movement within a polygon or with collision avoidance. It deals with fights which happen off screen, but not those that happen on screen.
|
||||
|
||||
## What do I mean by the client?
|
||||
|
||||
There is something that renders an interesting and lively display of the part of the game world that the player can see from their current position. This display has to run without significant pauses — it's not OK, for example, for all conversation to stop suddenly in a market place just because the server is garbage collecting.
|
||||
|
||||
The client is written in some high level game engine system, possibly Unreal Engine (although for ideological reasons I'd prefer an open source one).
|
||||
|
||||
The client/performer renders and animates everything the player character can see, and performs every sound the player character can hear. In doing this it is responsible for
|
||||
|
||||
1. The rendering of landscape, vegetation, buildings, furniture, and everything else that is fixed within the visible scene;
|
||||
2. The animation of everything which moves within the visible scene, and, to facilitate this, detailed route planning and collision avoidance;
|
||||
3. The performance of all speech acts and gestures, all musical performance, and the playing of all [foley](https://en.wikipedia.org/wiki/Foley_(filmmaking)) sounds;
|
||||
4. Combat which happens in the field of view, including specifically all combat (including sparring) involving the player character. This means that the client/performer is the bit of the system which decides what blows are struck and whether they hit their targets, and consequently which character wins each fight. It reports this information back to the server.
|
||||
|
||||
|
||||
|
63
doc/Dynamic-consequences.md
Normal file
63
doc/Dynamic-consequences.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
# On the consequences of a dynamic game environment for storytelling
|
||||
|
||||
First, a framing disclaimer: in [Racundra's First Cruise](https://books.google.co.uk/books?id=Ol1-DwAAQBAJ&lpg=PP1&pg=PT77#v=twopage&q&f=false), Arthur Ransome describes coming across a half built — and by the time he saw it, already obsolete — wooden sailing ship, in a Baltic forest. An old man was building it, by himself. He had been building it since he had been a young man. It's clear that Ransome believed the ship would never be finished. It's not clear whether the old man believed that it would, but nevertheless he was building it.
|
||||
|
||||
I will never build a complete version of The Great Game; it will probably never even be a playable prototype. It is a minor side-project of someone who
|
||||
|
||||
1. Is old and ill, and consequently has inconsistent levels of energy and concentration;
|
||||
2. Has other things to do in the real world which necessarily take precedence.
|
||||
|
||||
Nevertheless, in making design choices I want to specify something which could be built, which could, except for the technical innovations I'm trying myself to build, be built with the existing state of the art, and which if built, would be engaging and interesting to play.
|
||||
|
||||
The defining characteristic of Role Playing Games — the subcategory of games in which I am interested — is that the actions, decisions and choices of the player make a significant difference to the outcome of the plot, significantly affect change in the world. This already raises challenges for the cinematic elements in telling the game story, and those cinematic elements are one of the key rewards to the player, one of the elements of the game's presentation which most build, and hold, player engagement. These challenges are clearly expressed in two very good videos I've watched recently: [Who's Commanding Shepard in Mass Effect?](https://youtu.be/bm0S4cn_rfw), which discusses how much control the player actually has/should have over the decisions of the character they play as; and [What Happened with Mass Effect Andromeda’s Animation?](https://youtu.be/NmLPpcVQFJM), which discusses how the more control the player has, the bigger the task of authoring animation of all conversations and plot events becomes.
|
||||
|
||||
There are two key innovations I want to make in The Great Game which set it apart from existing Role Playing Games, both of which make the production of engaging cinematic presentation of conversation more difficult, and I'll handle each in turn. But before I do, there's something I need to make clear about the nature of video games themselves: what they are for. Video games are a vehicle to tell stories, to convey narrative. They're a rich vehicle, because the narrative is not fixed: it is at least to some degree mutable, responsive to the input of the audience: the player.
|
||||
|
||||
Clear? Let's move on.
|
||||
|
||||
The innovations I am interested in are
|
||||
|
||||
## Unconstrained natural speech input/output
|
||||
|
||||
I want the player to be able to interact with non-player characters (and, indeed, potentially with other player characters, in a multi-player context) simply by speaking to them. This means that the things the player character says cannot be scripted: there is no way for the game designer to predict the full repertoire of the player's input. It also means that the game must construct, and put into the mouth of the non-player character being addressed, an appropriate response, given
|
||||
|
||||
1. The speech interpretation engine's interpretation of what it is the player said;
|
||||
2. The immediate game and plot context;
|
||||
3. The particular non-player character addressed's knowledge of the game world;
|
||||
4. The particular non-player character's attitude towards the player;
|
||||
5. The particular non-player character's speech idiosyncracies, dialect, and voice
|
||||
|
||||
and it must be pretty clear that the full range of potential responses is extremely large. Consequently, it's impossible that all non-player character speech acts can be voice acted; rather, this sort of generated speech must be synthesised. But a consequence of this is that the non-player character's facial animation during the conversation also cannot be motion captured from a human actor; rather, [it, too, must be synthesized](https://youtu.be/fa3_Mfqu8KA).
|
||||
|
||||
This doesn't mean that speech acts by non-player characters which make plot points or advance the narrative can't be voice acted, but it does mean that the voice acting must be consistent with the simulated voice used for that non-player character — which is to say, probably, that the non-player character must use a synthetic voice derived from the voice performance of that particular voice actor in that role.
|
||||
|
||||
**Note that** this has interesting consequences for social equity with regard to those whose current profession is voice acting video games. Automating work people do generally has the consequence of putting those people out of work, or at least of making their work less valuable and consequently less remunerative. Almost everyone who has worked in software has to some extent done this. I'm not avoiding or ignoring the ethical issue here. I would argue in mitigation that because games of the type I am suggesting can never be voice acted, I'm not replacing work any real actors will ever do, but that is tendentious since if games of this sort are built and are successful they will compete for audience attention with games which are voice acted.
|
||||
|
||||
## Dynamic game environment
|
||||
|
||||
Modern Role Playing Games are, in effect, extremely complex state machines: if you do the same things in the same sequence, the same outcomes will always occur. In a world full of monsters, bandits, warring armies and other dangers, the same quest givers will be in the same places at the same times. They are clockwork worlds, filled with clockwork automata. Of course, this has the advantage that is makes testing easier — and in a game with a complex branching narrative and many quests, testing is inevitably hard.
|
||||
|
||||
Interestingly, [Kenshi](https://lofigames.com/) — a game I'm increasingly impressed and influenced by — is not quite clockwork in this sense. As the player upsets the equilibrium of the game's political economy, factions not impacted negatively will move against competing factions which are impacted negatively, in a way which *may* be scripted, but it's so well done it's hard to tell.
|
||||
|
||||
My vision for The Great Game is different. It is that the economy — and with it, the day to day choices of non-player characters — should be modelled. This means, non-player characters may unexpectedly die. Of course, you could implement a tag for plot-relevant characters which prevents them being killed (except when required by the plot).
|
||||
|
||||
## Plot follows player
|
||||
|
||||
As Role Playing Games have moved towards open worlds — where the player's movement in the environment is relatively unconstrained — the clockwork has become strained. The player has to get to particular locations where particular events happen, and so the player has to be very heavily signposted. Sometimes the mark you have to hit to trigger the next advance of the plot can be extremely awkward; [an example from Cyberpunk 2077](https://youtu.be/GEYkuctBUYE?t=2990) is finding the right spot, in the quest 'They Won't Go When I Go', to trigger the button which raises the cross.
|
||||
|
||||
Another solution — which I'd like to explore — is 'plot follows character'. The player is free to wander at will in the world, and plot relevant events will happen on their path. And by that I don't mean that we associate a set of non-player characters which each quest — as current Role Playing Games do — and then uproot the whole set from wherever they normally live in the world and dump them down in the player's path; but rather, for each role in a quest or plot event, we define a set of characteristics required to fulfil that role, and then, when the player comes to a place where there are a set of characters who have those characteristics, the quest or plot event will happen.
|
||||
|
||||
## Cut scenes, cinematics and rewarding the player
|
||||
|
||||
There's no doubt at all that 'cut scenes' — in effect, short movies spliced into game play during which the player has no decisions to make but can simply watch the scene unroll — are elements of modern games which players enjoy, and see to some extent as 'rewards'. And in many games, these are beautifully constructed works. It is a very widely held view that the quality of cutscenes depends to a large degree on human authorship. The choices I've made above:
|
||||
|
||||
1. We can't always know exactly what non-player characters will say (although perhaps we can in the context of cut scenes where the player has no input);
|
||||
2. We can't always know exactly which non-player characters will speak the lines;
|
||||
3. We can't predict what a non-player character will say in response to a question, or how long that will take;
|
||||
4. We can't always know where any particular plot event will take place;
|
||||
|
||||
each make the task of authoring an animation harder. The general summary of what I'm saying here is that, although in animating a conversation or cutscene what the animator is essentially animating is the skeletons of the characters, and, provided that all character models are rigged on essentially similar skeletons, substituting one character model for another in an animated scene isn't a huge issue, with so much unknowable it is impossible that hand-authoring will be practicable, and so a lot will depend on the quality of the conversation system not merely to to produce convincingly enunciated and emoted sound, but also appropriate character animation and attractive cinematography. As you will have learned from the Mass Effect analysis videos I linked to above, that's a big ask.
|
||||
|
||||
Essentially the gamble here is that players will find the much richer conversations, and consequent emergent gameplay, possible with non-player charcaters who have dynamic knowledge about their world sufficiently engaging to compensate for a less compelling cinematic experience. I believe that they would; but really the only way to find out would be to try.
|
||||
|
||||
Interestingly, an [early preview](https://youtu.be/VwwZx5t5MIc?t=327) of CD Project Red's [Cyberpunk 2077](https://www.cyberpunk.net/us/en/cyberpunk-2077) has relatively few cutscenes, suggesting that these very experienced storytellers don't feel they need cutscenes either to tell their story or maintain player engagement.
|
|
@ -6,7 +6,7 @@ Broadly this essay extends ideas presented in [Populating a game world](Populati
|
|||
|
||||
### Herdsfolk
|
||||
|
||||
Herdsfolk are nomadic; it's reasonable to think they'll bring their herds to market, rather than selling it lots of tiny markets. So in the spring, shepherds will visit specific towns at the edge of open land, to hold a shearing festival/carnevale; and that both shepherds and cattle herders will visit towns on the edge of open land to sell fatstock in the autumn.
|
||||
Herdsfolk are nomadic; it's reasonable to think they'll bring their herds to market, rather than selling at lots of tiny markets. So in the spring, shepherds will visit specific towns at the edge of open land, to hold a shearing festival/carnevale; and that both shepherds and cattle herders will visit towns on the edge of open land to sell fatstock in the autumn.
|
||||
|
||||
### Miners
|
||||
|
||||
|
@ -32,18 +32,22 @@ Farmers are settled. Farmers occupy standard runrig plots, but because they don'
|
|||
* fibres: linen, hemp and silk (from silk-moths in mulberry orchards)
|
||||
* possibly other stuff I've forgotten.
|
||||
|
||||
Farmers are all basically subsistence farmers, farming first to feed their own household and selling only surplus in the market.
|
||||
Farmers are all primarily subsistence farmers, farming first to feed their own household and selling only surplus in the market.
|
||||
|
||||
## Crafts
|
||||
|
||||
Crafts generally process primary goods into secondary goods - whether intermediate stages or final consumer items. Some elite 'crafts' deal with abstract primary goods like law and knowledge, and they may be seen as somewhat separate.
|
||||
Crafts generally process primary goods into secondary goods — whether intermediate stages or final consumer items. Some elite 'crafts' deal with abstract primary goods like law and knowledge, and they may be seen as somewhat separate.
|
||||
|
||||
A master craftsperson occupies a standard runrig plot, much like a farmer's plot. Like a farmer, a poor master crafter household will cultivate part of the plot to produce food for the house - at least grow vegetables and keep hens. However, as the crafter takes on apprentices and journeymen - and gets richer - more buildings will be required as accommodation, workshop space and materials stores.
|
||||
A master craftsperson may occupy a standard runrig plot, much like a farmer's plot. Like a farmer, a poor master crafter household will cultivate part of the plot to produce food for the house — at least grow vegetables and keep hens. However, as the crafter takes on apprentices and journeymen — and gets richer — more buildings will be required as accommodation, workshop space and materials stores.
|
||||
|
||||
Generally, primary goods aren't transported over land - because overland transport is expensive, by the time they've been transported they're no longer low cost goods. So often the craftspeople who process primary produce into at least commodity intermediate forms will live close to the source of the primary goods.
|
||||
Also, Tchahua is much more a gold-rush town than an organic, grew over hundreds of years sort of town, so it is not ex-runrig; and additionally the original settlement was probably along the river bank, land which has now been redeveloped as warehouses and as rich merchant residences. Generally, town house plots are small from the get go.
|
||||
|
||||
Hans'hua is again an exception from normal organic development, as it has no agricultural land close to the city at all.
|
||||
|
||||
Generally, primary goods aren't transported over land — because overland transport is expensive, by the time they've been transported they're no longer low cost goods. So often the craftspeople who process primary produce into at least commodity intermediate forms will live close to the source of the primary goods.
|
||||
|
||||
So, for example, the town(s) where the shepherds hold their shearing fairs will tend to have a lot of weavers. While around mines there will be smelters producing ingots and bar stock to be marketed to smiths all over the place, there will also be smiths close to the mines producing commodity tools and weapons.
|
||||
|
||||
See the table in Populating a game world.
|
||||
See the tables in [Populating a game world](Populating-a-game-world.html).
|
||||
|
||||
|
11
doc/Further-reading.md
Normal file
11
doc/Further-reading.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Further Reading (and watching)
|
||||
|
||||
Work by other people which is relevant to what I'm doing, and which I should study.
|
||||
|
||||
## Modelling the natural environment
|
||||
|
||||
1. [Synthetic Silviculture: Multi-scale Modeling of Plant Ecosystems](https://storage.googleapis.com/pirk.io/projects/synthetic_silviculture/index.html) — see also [this video](https://youtu.be/8YOpFsZsR9w).
|
||||
|
||||
## Systemic games
|
||||
|
||||
1. [This video](https://youtu.be/SnpAAX9CkIc) is thought provoking with excellent examples.
|
7
doc/Game-engine-integration.md
Normal file
7
doc/Game-engine-integration.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Game-engine integration (unfinished)
|
||||
|
||||
To build a game using these ideas we need a lot of things that are well understood and already implemented: rendering a world, moving models of characters in a world, and so on. This collection of technologies which allow us to realise an interactive realisation of a world is typically called a game engine.
|
||||
|
||||
It's my intention that the bits that I add to the mix should be open source in the hard sense of that phrase, fully free software released under GPL. They cannot therfore be directly linked to a proprietary game engine.
|
||||
|
||||
But the current state of play is that the best and easiest to work with game engines are not open source; and while I could build a demo game using, for example, the [Godot engine](https://godotengine.org/) or [jMonkeyEngine](https://jmonkeyengine.org/) the result wouldn't be as compelling and *I believe* the effort would be more considerable than if I use [Unreal Engine](https://www.unrealengine.com/en-US), which is my current plan.
|
|
@ -2,25 +2,25 @@
|
|||
|
||||
The principles of game play which I'm looking for are a reaction against all I see as wrong in modern video games. So let's set out what these are:
|
||||
|
||||
1. Superpower: the player character has some special powers or skills that other characters in the game fo not have.
|
||||
1. **Superpower**: the player character has some special powers or skills that other characters in the game fo not have.
|
||||
|
||||
2. Special status: the player character is 'the chosen one', 'the hero', or even just 'the Witcher' from the very beginning, without having done anything to earn those titles.
|
||||
2. **Special status**: the player character is 'the chosen one', 'the hero', or even just 'the Witcher' from the very beginning, without having done anything to earn those titles.
|
||||
|
||||
3. Boss fights: some non-player characters have special, and specially strong, combat repertoire, and block progress in the game until you overcome them.
|
||||
3. **Boss fights**: some non-player characters have special, and specially strong, combat repertoire, and block progress in the game until you overcome them.
|
||||
|
||||
4. Psychokiller: completing the game necessarily involves beating many, many other characters in combat.
|
||||
4. **Psychokiller**: completing the game necessarily involves beating many, many other characters in combat.
|
||||
|
||||
5. Slaughterhouse: the main way to interact with other characters is to kill them.
|
||||
5. **Slaughterhouse**: the main way to interact with other characters is to kill them.
|
||||
|
||||
7. The Script is King: everything is scripted. The player either can't diverge from the script, or if they do, will find no interesting content.
|
||||
7. **The Script is King**: everything is scripted. The player either can't diverge from the script, or if they do, will find no interesting content.
|
||||
|
||||
6. Dumb and dumber: non-player characters, even important ones, have extremely limited vocal repertoire.
|
||||
6. **Dumb and dumber**: non-player characters, even important ones, have extremely limited vocal repertoire.
|
||||
|
||||
Of these, the last two, I think, are key: they are the root cause of the other problems. In fact, to take it further, the real key is the last. We talk a lot about 'Game AI', but really there's nothing remotely approaching artificial intelligence ins modern games. Non-player characters do not think; they do not learn; they do not reason; they do not know. They speak only from the script. And they speak only from the script because of the fetish for voice acting.
|
||||
Of these, the last two, I think, are key: they are the root cause of the other problems. In fact, to take it further, the real key is the last. We talk a lot about 'Game AI', but really there's nothing remotely approaching artificial intelligence in modern games. Non-player characters do not think; they do not learn; they do not reason; they do not know. They speak only from the script. And they speak only from the script because of the fetish for voice acting.
|
||||
|
||||
## Death to Dumb-Dumb
|
||||
## Death to Dum-Dum
|
||||
|
||||
As I've argued [elsewhere](), [repeatedly](), we can now generate a wide variety of naturalistic speaking voices, and have them narrate text. Now of course there's great deal of information conveyed in human vocal communication in addition to the words – of which emotion is only an example, although an important one. Generating voices with the right tone, the right emphasis, for different situations may be harder than I anticipate; there may be an '[uncanny valley](Uncanny_dialogue)' in which generated speech just sounds uncomfortably off.
|
||||
As I've argued [elsewhere](Voice-acting-considered-harmful.html), [repeatedly](Selecting_Character.html), we can now generate a wide variety of naturalistic speaking voices, and have them narrate text. Now of course there's great deal of information conveyed in human vocal communication in addition to the words – of which emotion is only an example, although an important one. Generating voices with the right tone, the right emphasis, for different situations may be harder than I anticipate; there may be an '[uncanny valley](Uncanny_dialogue.html)' in which generated speech just sounds uncomfortably off.
|
||||
|
||||
But it's a trade off. For possibly less than perfect vocal performance, you get the possibility of much richer repertoire. You get not only the possibility that non-player characters can talk about the weather, or gossip about their neighbours, or give you directions to local places of interest. You get the possibility that a non-player character's attitude to you may be conditioned by the fact that they've heard that you stole from their second cousin, or that you killed an outlaw who'd raped one of their friends.
|
||||
|
||||
|
@ -28,7 +28,7 @@ Suddenly, they can have attitudes about things that happen in the world, opinion
|
|||
|
||||
And with the emergence of intelligent behaviour comes the emergence of possibilities for negotiation, for diplomacy, for dynamic, unscripted, friendships and romances. Which means, there are things you can do to interact with every non-player character, even ones who are not 'plot' characters, other than just kill them.
|
||||
|
||||
And as now gameplay possibilities emerge, as new stories emerge organically out of the dynamically changing relationships between non-player characters in the world, the need for scripting decreases.
|
||||
And as new gameplay possibilities emerge, as new stories emerge organically out of the dynamically changing relationships between non-player characters in the world, the need for scripting decreases.
|
||||
|
||||
The problem with scripting is that it greatly limits player agency. The story can only have one of a few predetermined -- literally, scripted -- endings. This is clearly expressed in [a review of Red Dead Redemption 2](https://youtu.be/_JRikiQyzLA) which I recomment to you; but is equally true of almost all other games.
|
||||
|
||||
|
@ -36,5 +36,5 @@ Dynamic side quests have fallen into disfavour, because, when they've been tried
|
|||
|
||||
## Death to Psycho-Killer
|
||||
|
||||
If the main way a player can interact with non-player characters is to kill them, and if the player doesn't have a systematic combat advantage over non-player characters, then it's going to be a short game. This is why players in many or most video games do start with a systematic combat advantage, and that combat advantage tends to increase over the course of the game as the player becomes more proficient with the combat system, and acquires better weapons, armour and combat buffs. This in turn means that to keep combat 'interesting', the game either has to through larger and larger armies of 'bad' non-player characters against the player – a fault seen at its worst in [Dragon Age 2](https://youtu.be/Sc8Bn8yqPYQ?t=3150).
|
||||
If the main way a player can interact with non-player characters is to kill them, and if the player doesn't have a systematic combat advantage over non-player characters, then it's going to be a short game. This is why players in many or most video games do start with a systematic combat advantage, and that combat advantage tends to increase over the course of the game as the player becomes more proficient with the combat system, and acquires better weapons, armour and combat buffs. This in turn means that to keep combat 'interesting', the game has to through larger and larger armies of 'bad' non-player characters against the player – a fault seen at its worst in [Dragon Age 2](https://youtu.be/Sc8Bn8yqPYQ?t=3150).
|
||||
|
||||
|
|
61
doc/Genetic-buildings.md
Normal file
61
doc/Genetic-buildings.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# Genetic Buildings
|
||||
|
||||
### Building selection based on location
|
||||
|
||||
The objective of this note is to create a landscape with varied and believable buildings, with the minimum possible data storage per instance.
|
||||
|
||||
Like plants, buildings will 'grow' from a seed which has northing and easting attributes. These locate a position on the map. Again, like trees, some aspects of the building type selector are location based. Aspects of the location which are relevant to building type are
|
||||
|
||||
* **elevation** — derived from the map location by interpolation from grid. The actual interpolation algorithm is probably some form of spline, but in any case it's the same one as for everything else.
|
||||
* **orientation of slope** — derived by taking altitude at four corners of a 100 metre square centred on the seed point, and then taking the highest and lowest of these. If highest is northwest, lowest is southeast, the slope is considered to be oriented southeast; if highest is northwest and lowest southwest, the orientation is considered to be south, and so on. Eight orientation values are sufficient.
|
||||
* **gradient of slope** — derived from the difference in altitude across the same 100 metre square
|
||||
* **neighbours** — number of other buildings in 500 metre square centred on seed point.
|
||||
|
||||
The reason orientation is relevant is exactly the same as the reason it's relevant to trees. West facing slopes are assumed wetter (coriolis winds), so grow trees better, so better availability of better quality timber, so a higher probability of timber as a primary building material. But also, in areas of higher rainfall, rain shedding is an important consideration, so a higher value is placed on pitched roofs.
|
||||
|
||||
So you have the following general relationships
|
||||
|
||||
* west (or southwest or northwest) facing, moderate gradient, moderate altitude: high probability of timber construction; construction techniques involving large timbers (e.g. cruck frame); greater probability of shingled roofs;
|
||||
* west (or southwest or northwest) facing, moderate gradient, higher altitude or northern latitude: high probability of building styles adapted to straight-trunk conifers, e.g. log cabins, stave buildings; greater probability of shingled roofs;
|
||||
* east facing, generally: greater probability of flat roofs;
|
||||
* steeper gradients: greater probability of stone buildings (steeper gradients = shallower topsoil and greater ease of quarrying = access to stone); greater probability of slate roofs;
|
||||
* shallower gradients: greater probability of mud, cobb, brick or wattle-and-daub as building materials; greater probability of thatch or turf roofs;
|
||||
* Higher number of neighbours: higher probability of two or more stories;
|
||||
|
||||
These factors allow classes of building to be selected. Having got past that point, we need to consider how classes of genetic building can work.
|
||||
|
||||
### Rectangular genetic buildings
|
||||
|
||||
Some genetic buildings will have cells with rectangular plan. This doesn't mean that genetic buildings are required to have rectangular cells, but they provide a starting point for discussion. For a given class of building (for example, timber frame), a number of prototype models of cells exist. These models are fully realised three dimensional models. Possibly all cells belonging to the building class have two open ends, and end walls exist as separate models; equally possibly, some cells have only one extensible end. In any case, a building will not normally comprise a single cell. Normally it will comprise multiple cells. So the cells belonging to a particular building class will be designed to 'plug together'. Multi story building classes will have some cells which are specifically ground floor only (flat ceiling, no roof), and such cells will always have an upper floor cell added above them. Where an upper floor cell has an outside door, an outside stair will automatically be added.
|
||||
|
||||
### Cell mutability
|
||||
|
||||
Although cell models are repeatedly reused they don't have to look the same every time they are reused. Within limits, every cell can be stretched along any of its three axes. Obviously, the degree of stretch on a given axis for every cell in a given building must be the same, otherwise they won't line up. Another mutable area is skinning — it may be possible to have alternate skins for cells, and even if there are not alternate skins, it will be possible to mutably darken, lighten or otherwise tint the skins used, within ranges which are appropriate to the materials represented. Obviously there are limits to stretching — timber comes in only such a length, stone lintels will only support such a span.
|
||||
|
||||
### Functional cells
|
||||
|
||||
Some trade functions require cells of particular kinds. Thus a smith needs a working building with one cell which is explicitly a forge. A water mill must have one cell which explicitly houses the mill gear. A forge cell or a waterwheel cell should never appear in weavers workshop. But most cells are not dedicated in this way. A bedroom cell is a bedroom cell, more or less; wealth may alter how it is furnished, but it may appear in any dwelling. Similarly, except for the very wealthy, a living cell is pretty much a living cell. And any building may incorporate a storage cell. If a given building class has twelve distinct 'generic' cells' and half a dozen distinct functional cells, and if buildings in the class average four cells each, then ignoring variance caused by skin mutability, a street of fifty buildings could have every one different.
|
||||
|
||||
### Reproducibility
|
||||
|
||||
It's critical that if a player visits a location, leaves it, and then returns, the buildings should not all have changed. So it must be possible to repeatedly reproduce the building at the location (this, of course, applies to other procedural scene dressing, such as trees, roads, boundaries, bridges and so on). This is possible if a deterministic random number generator is used which is seeded from the latitude and longitude attributes of the location. Other attributes which should be cached on the seed even though they are determined procedurally when the building is first instantiated include building class, purpose, and wealth. Using these attributes and the deterministic random number generator, the same building can be reproduced on the same site each time it is visited, with a very small amount of data stored.
|
||||
|
||||
Buildings will normally be built at the edge of the associated land holding. If an edge of the land holding adjoins a road, then the building will be built with one long side aligned to the road. Otherwise, the building will be built at right angles to the orientation of the slope. The orientation will be 'frozen' once the building has been instantiated and will be cached on the seed.
|
||||
|
||||
So, to build a building, use the following algorithm:
|
||||
|
||||
Seed the random number generator with latitude and longitude
|
||||
|
||||
```
|
||||
while ( building value is less than wealth) {
|
||||
select a cell selected from the building class using the next number from the random number generator modulo the number of generic cells in the class;
|
||||
if the selected cell is not inappropriate to the building's function {
|
||||
fit the cell to the building at the point determined by a deterministic algorithm
|
||||
furnish cell using the random number generator to determine
|
||||
furnishing types and locations from a selection appropriate to the cell
|
||||
if the selected cell was not a top story cell {
|
||||
add a requirement that the next cell selected must be an upper story cell}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1,33 +1,33 @@
|
|||
# Gossip, scripted plot, and Johnny Silverhand
|
||||
|
||||
I've been writing literally for years -- since [Voice acting considered harmful](Voice-acting-considered-harmful.html) in 2015 -- about game worlds in which the player speaks to non-player characters just by speaking the words they choose in their normal voice, and the non-player character replies using a pipeline that goes, essentially,
|
||||
I've been writing literally for years — since [Voice acting considered harmful](Voice-acting-considered-harmful.md) in 2015 — about game worlds in which the player speaks to non-player characters just by speaking the words they choose in their normal voice, and the non-player character replies using a pipeline that goes, essentially,
|
||||
|
||||
1. Alexa/Siri style speech interpretation;
|
||||
2. A decision on whether to co-operate based on the particular NPC's general demeanor and particular attitude to the player;
|
||||
3. A search of the game state and lore for relevant information;
|
||||
4. A filtering of the results based on what the particular NPC can be expected to know;
|
||||
5. Generation of a textual response from those results based on a library of templates which defines the particular NPC's dialect and style of speech;
|
||||
6. Production of audio using a [Lyrebird]{https://www.descript.com/overdub?lyrebird=true) style generated voice.
|
||||
6. Production of audio using a [Lyrebird](https://www.descript.com/overdub?lyrebird=true) style generated voice.
|
||||
|
||||
As I've argued before, the game engine necessarily knows everything about the lore, and the current state, of the game world. It would be possible for any non-player character to answer literally any question about the game world, from who was mayor of Night City in 2020 to who lives in the apartment one floor up from yours, to what the weather is like in North Oaks just now.
|
||||
|
||||
What individual characters know should, of course, be more limited. People who live in Japantown or Heywood are unlikely to know who lives in a particular apartment in Watson; only real old timers, like Rogue, are likely to remember who was mayor fifty years ago. That's the reason for filtering; but the filtering really isn't a big deal.
|
||||
|
||||
Again, the generation of distinct voices for hundreds of non-player characters isn't any longer a big deal. Distinct social groups -- the corpos, and the different gangs such as Maelstrom or the Mox, will have their own argot, their own slang, their own habitual figures of speech which can be encoded into template libraries, while technologies like Lyrebird can produce an infinite range of realistic-sounding voices.
|
||||
Again, the generation of distinct voices for hundreds of non-player characters isn't any longer a big deal. Distinct social groups — the corpos, and the different gangs such as Maelstrom or the Mox, will have their own argot, their own slang, their own habitual figures of speech which can be encoded into template libraries, while technologies like Lyrebird can produce an infinite range of realistic-sounding voices.
|
||||
|
||||
In particular, they can mimic real voices. They can mimic the voices of real actors. They can mimic [Keanu Reeves](https://cyberpunk.fandom.com/wiki/Keanu_Reeves).
|
||||
In particular, they can mimic real voices. They can mimic the voices of real actors. They can mimic [Keanu Reeves](https://cyberpunk.fandom.com/wiki/Keanu_Reeves). (Interestingly, since I first wrote this note, CD Projekt Red have used Lyrebird-like technology to [resurrect a voice actor](https://www.theverge.com/2023/10/13/23915535/cyberpunk-2077-phantom-liberty-polish-voice-actor-ai-ripperdock-viktor-vektor) in Phantom Liberty, proving that the technology is good enough).
|
||||
|
||||
So: how do you integrate this free form 'you can say anything to any character' style of play with scripted plot?
|
||||
|
||||
Obviously, my vision -- as I've set out in [Organic Quests](Organic_Quests.md) -- is that many quests should emerge organically from modelling the lives, activities and motivations of non-player characters. But that's a radical vision and not one you can really expect many people to buy into until it has been demonstrated to work. I think that investors are still going to want to have confidence that there's something exciting in the game for players to engage with, and I think directors are still going to want to tell the stories they want to tell.
|
||||
Obviously, my vision — as I've set out in [Organic Quests](Organic_Quests.md) — is that many quests should emerge organically from modelling the lives, activities and motivations of non-player characters. But that's a radical vision and not one you can really expect many people to buy into until it has been demonstrated to work. I think that investors are still going to want to have confidence that there's something exciting in the game for players to engage with, and I think directors are still going to want to tell the stories they want to tell.
|
||||
|
||||
So if I'm to sell the idea of free-form speech interaction with characters in the game world, I need an account of how it works with scripted characters voiced by high value actors in a scripted plot. I'm picking Johnny Silverhand as a core example, here, because I think he presents particular challenges.
|
||||
|
||||
But I also think these challenges can be addressed very easily.
|
||||
|
||||
In [Cyberpunk 2077](https://www.cyberpunk.net/), the player can't just go and find Johnny Silverhand, to speak to him. On the contrary, Johnny will just appear when the script calls for him to appear, and when he does he'll always initiate conversation. When a plot NPC initiates conversation with the player, the game could show -- as it does now -- a menu of things the player can say, with the implicit promise that selecting any one of these things will at least bring an interesting response which will expand one's knowledge of that character or of the lore.
|
||||
In [Cyberpunk 2077](https://www.cyberpunk.net/), the player can't just go and find Johnny Silverhand, to speak to him. On the contrary, Johnny will just appear when the script calls for him to appear, and when he does he'll always initiate conversation. When a plot NPC initiates conversation with the player, the game could show — as it does now — a menu of things the player can say, with the implicit promise that selecting any one of these things will at least bring an interesting response which will expand one's knowledge of that character or of the lore.
|
||||
|
||||
Just as the player does now, the player in a game with free form speech interaction could choose to say one of the things presented in the menu, and the implicit contract -- that this would lead to a new revelation, or would advance the plot -- would remain unchanged. But the player could also choose to go off script, to take the conversation in an unscripted direction, or just to end it.
|
||||
Just as the player does now, the player in a game with free form speech interaction could choose to say one of the things presented in the menu, and the implicit contract — that this would lead to a new revelation, or would advance the plot — would remain unchanged. But the player could also choose to go off script, to take the conversation in an unscripted direction, or just to end it.
|
||||
|
||||
It should be said that in Cyberpunk 2077, unlike some other games, the player already has the choice to abruptly break off conversations, even with plot characters, so how the game handles breaking off the conversation does not need to change.
|
||||
|
||||
|
@ -41,7 +41,7 @@ Well, the first and obvious thing is to parse the unscripted response to see whe
|
|||
>
|
||||
> **V**: Yes, dammit.
|
||||
|
||||
But the second thing is to respond to the response exactly as the non-player character would if the player had initiated the conversation, using the pipeline given at the beginning of this essay. Of course, in the special case of Johnny Silverhand, he is -- at least initially -- decidedly hostile and extremely selfish, so his response will typically come at step two in the pipeline:
|
||||
But the second thing is to respond to the response exactly as the non-player character would if the player had initiated the conversation, using the pipeline given at the beginning of this essay. Of course, in the special case of Johnny Silverhand, he is — at least initially — decidedly hostile and extremely selfish, so his response will typically come at step two in the pipeline:
|
||||
|
||||
> **V**: Hey, Johnny, what's the quickest way from here to Jig Jig Street?
|
||||
>
|
||||
|
|
59
doc/MVP-Roadmap.md
Normal file
59
doc/MVP-Roadmap.md
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Minimum Viable Product, and a road map
|
||||
|
||||
Right, I'm bogged down thinking about the immensity of what I want to build, so I'm achieving nothing. So the first thing I need to state is what the Minimum Viable Product is, and the second is to outline a rough road map which takes us forwards a few steps from the MVP.
|
||||
|
||||
The core idea here is to have a game world in which you can just say anything you like to game characters, and they can say sensible things back.
|
||||
|
||||
But actually, I know that speech to text can be reasonably effectively done; and I believe with a slightly lower degree of confidence that text to convincing speech can also be done.
|
||||
|
||||
I also know that the movement of a character around a convincing three dimensional representation of a world can be done, but that a great deal of effort is needed to build that world.
|
||||
|
||||
The minimum viable product does not need to demonstrate features which people have reasonable confidence can be done. What I need to demonstrate is the things which people haven't seen done, or haven't seen done well.
|
||||
|
||||
## Prototype one: the minimum viable product
|
||||
|
||||
The minimum viable product can have just a text adventure style interface:
|
||||
|
||||
> You are in the market square. It is mid morning. To the north is the guild hall; to the east there are market stalls; to the south is the residence; to the west is the bridge gate.
|
||||
|
||||
> There is a merchant here; there is a guardsman here.
|
||||
|
||||
To which the user can type (for example)
|
||||
|
||||
> Say to the guardsman, "Can you direct me to Master Dalwhiel's house?"
|
||||
|
||||
Within that interface, you should be able to interact with characters who:
|
||||
|
||||
1. have different levels of knowledge of the world, partly driven by their age, trade and personal history;
|
||||
2. move about and exchange gossip, even when the player is not present to see/hear this;
|
||||
3. have different attitudes towards the player and other characters, which will be modified by what they learn in gossip;
|
||||
4. have their own hierarchies of needs, which they make plans to satisfy;
|
||||
5. have homes and trades;
|
||||
6. will respond to speech addressed to them by the player depending on their attitude to the player, how busy they are and their knowledge of the world; and
|
||||
7. as a stretch goal, will have different dialects in which they will express their responses to the player.
|
||||
|
||||
There should be one or two multiple decision point quests in this world which can be resolved by talking to characters.
|
||||
|
||||
## Prototype two: adding organic quests
|
||||
|
||||
Extends prototype one only by adding [organic quests](Organic_Quests.html).
|
||||
|
||||
## Prototype three: voice interaction
|
||||
|
||||
Extends prototype two by adding speech to text, so that the player can directly talk (via a microphone) to characters, and text to speech, so that the system can voice the characters' responses.
|
||||
|
||||
Different characters should have different voices.
|
||||
|
||||
## Prototype four: performative speech
|
||||
|
||||
This one is hard because I'm not absolutely sure how I can do it, but I need characters' voices to convey emotion; the player needs to know from their voice whether they are angry, or frightened, or impatient, or bored.
|
||||
|
||||
There is a [W3C specification](https://www.w3.org/TR/speech-synthesis11/) for an XML markup for speech performance, and I can certainly generate that, but I'd need to find a text-to-speech library which could consume it. There's also a separate [specification](https://www.w3.org/TR/pronunciation-lexicon/) to associate pronunciations with lexical tokens, which is also potentially useful, especially for names.
|
||||
|
||||
Google has a '[Cloud Text-to-Speech](https://cloud.google.com/text-to-speech/docs/ssml)' service which understands SSML and might be good enough for a demo but is more likely just embarrassingly bad.
|
||||
|
||||
## Prototype five traversible world
|
||||
|
||||
Now, a small section of a three dimensional open world, with at this stage simple block buildings that the player cannot enter, within which the characters act out their lives.
|
||||
|
||||
Stretch goal, [JALI](https://www.youtube.com/watch?v=uFIxiz0jwRE)-like lip sync.
|
29
doc/Modelling_democracy_and_morale.md
Normal file
29
doc/Modelling_democracy_and_morale.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# The Red Company: modelling democracy and morale (unfinished)
|
||||
|
||||
## Background
|
||||
|
||||
The Great Game exists as a project on two levels. One one level, it's a framework for building algorithms to build much more vibrant, and thus enjoyable game worlds; at another level, it's about building a particular world, in which I want to tell stories.
|
||||
|
||||
The world in which I want to tell stories is a world which is based roughly on late bronze age to medieval Europe. It's a world in which the region known as 'The Coast' -- the southern littoral of the continent -- had been a mostly-peaceful matrideic dispersed agrarian tribal society, which had been invaded some hundreds of years past by a warrior tribe with substantially better military technology.
|
||||
|
||||
These warrior tribesmen have settled down as local tyrants or robber barons, parasitising on the indigenous communities, and have evolved into an aristocratic ('Ariston') class. In the meantime, a mercantile class has grown up and established important long distance overland trade routes; and significant towns (called 'cities', but of only at most a few tens of thousand inhabitants) have grown up around markets.
|
||||
|
||||
These mercantile cities have been under the governance of powerful aristons known as tyrranoi, and the cities under their tyrranoi have competed militarily as well as commercially for control of strategic features on trade routes, such as bridges, fords, oases, mountain passes, and so on.
|
||||
|
||||
In the very earliest days of the warrior invasion, the warriors themselves fought against the indigenous peoples, who had very limited military equipment and tactics. Later, as they settled into Aristons, they fought by leading feudal levies of partially-trained peasants. Over the past hundred years or so, mercenary companies have emerged of specialist, trained warriors, and because these have more fighting experience (and often better equipment) they tend to beat feudal levies. These mercenary companies are base loosely on the condottierri of fourteenth century Italy.
|
||||
|
||||
So more and more, tyrranoi, rather than leading their own feudal levies, instead tax their peasantry and mercantile class more and hire condottierri to fight their wars.
|
||||
|
||||
Mercenary companies evolve out of feudal levies, and in the period of The Great Game, are mostly owned and led by aristons who employ their soldiers by paying them a wage.
|
||||
|
||||
One company, the Red Company, has become essentially a workers' co-op, after its former ariston leader fled in the course of a battle which looked like an inevitable defeat (but which the company, without him, won). In this company, soldiers are paid a salary, probably lower than salaries in other companies, but also at the end of the year get a share in the profits. The soldiers are organised into squads of eight who elect their own sergeants; squads are organised into companies of eight squads, and the sergeants elect the captain; companies are organised into legions of eight companies, and the captains elect the captain-general.
|
||||
|
||||
However, while in combat this represents a chain of command, out of combat it is much more a delegate structure; when making significant decisions, the captains general will consult with the captains who will consult with the sergeants who will consult with the soldiers.
|
||||
|
||||
One of the themes of the stories I want to tell is that this more democratic structure contributes to higher morale and hence to greater military success. I could model this by just making membership of the Red Company a factor in the function which computes morale. However...
|
||||
|
||||
## Modelling democracy
|
||||
|
||||
If each individual character has a hierarchy of needs, and plans actions based on that hierarchy of needs, then they have the mechanism in place to choose which of two options better conforms to their hierarchy of needs.
|
||||
|
||||
This implies that soldiers are likely to vote for the people (or ideas presented by the people) they consider most competent and/or most trustworthy. Which comes back to modelling reputation; which comes back to the [gossip](the-great-game.gossip.gossip.html).
|
|
@ -1,4 +1,4 @@
|
|||
# Modelling trading cost and risk
|
||||
# Modelling trading cost and risk (unfinished)
|
||||
|
||||
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.
|
||||
|
26
doc/Not_my_problem.md
Normal file
26
doc/Not_my_problem.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Not my problem
|
||||
|
||||
## Introduction
|
||||
|
||||
This document is essentially a catalogue of side-tracks which I do not have to go down when implementing The Great Game. Solved problems; or problems which are common to many other games, so if I don't solve them someone else will. The object of doing this is to work down to a constrained set of problems which are genuinely things I'm trying to innovate, which I should focus on; which essentially come down to
|
||||
|
||||
1. Gossip
|
||||
2. Reputation
|
||||
3. Dynamic character motivation and action, and hence
|
||||
4. Dynamic economy, and
|
||||
5. Dynamic plot
|
||||
6. Procedural ('genetic') buildings.
|
||||
|
||||
(Note that although procedural vegetation is in principle a solved problem and so I don't need to solve it, I need repeatable procedural vegetation so I need to be a bit careful about the procedural vegetation library I pick).
|
||||
|
||||
## Animation
|
||||
|
||||
I envisage a well rendered three dimensional world in which many non-player characters interact with one another and with the player character. All of my characters are either human, quadrupeds, or birds (my dragons animate like very large birds). The humans are all just human; there are infants, children, adolescents, youths, adults, elderly; there are multiple racial types — but they're all human. Systems for creating varied distinct human models exist; systems for animating them exist; systems for applying and animating clothing exist. Even systems for animating continuous speech exist.
|
||||
|
||||
## Rendering
|
||||
|
||||
Ideally I'd like a stylised, not-quite-photorealistic render, because such things age, in my opinion, better than things which do seek to be photorealistic. But actually that does not matter very much; the game won't be made or broken by its rendering. Photorealistic renders are sort of the current default, that most game engines.
|
||||
|
||||
## Continuous Open World
|
||||
|
||||
I've done a great deal of thinking about how to render a continuous open world over the years, and I think at least some of it is reasonably good; but I haven't actually written any code, and in the same time period other people have written continuous open world libraries which do work, so I'd be much better choosing an existing one.
|
|
@ -1,4 +1,4 @@
|
|||
# On Dying
|
||||
# On Dying, and Injury
|
||||
|
||||
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.
|
||||
|
||||
|
@ -11,3 +11,17 @@ Time has passed; events in the game world have moved on. You can talk to your sa
|
|||
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.
|
||||
|
||||
## Injury
|
||||
|
||||
Similarly to death, injury must have meaning. Any injury takes time to recover from. It takes a certain amount of time if you're able to rest somewhere safe, and considerably longer if you're not. If you fight while injured, you'll have less strength, less stramina, and probably also less agility. Depending where you're injured, there will be certain things you cannot do. If you fight while injured, also, your recovery time will be extended, even if you take no further injury.
|
||||
|
||||
Some serious injuries will lead to permanent scarring, and permanent loss of agility; you'll be just slightly slower in fights.
|
||||
|
||||
It should be said that [Kenshi](https://lofigames.com/) — a game I've only recently become aware of and greatly admire — handles all of this extremely well, and is worth studying.
|
||||
|
||||
## Reciprocity
|
||||
|
||||
If the player is going to depend on good samaritans for rescue after losing a fight, then there must be at least a social convention that people should assist people found injured on the wayside. Consequently, if the player fails to do this, that should in itself become a 'gossip' event which will lower the player's reputation with non-player characters.
|
||||
|
||||
On the other hand, helping NPCs found injured at the wayside can be another category of [organic quest](Organic_Quests.html), as a special subcategory of an escort quest.
|
73
doc/On-sex-and-sexual-violence.md
Normal file
73
doc/On-sex-and-sexual-violence.md
Normal file
|
@ -0,0 +1,73 @@
|
|||
# On Sex, and Sexual Violence, in Games
|
||||
|
||||
For me the purpose of games is to provide worlds in which players can explore moral actions, and the consequences of moral actions. Sexual violence is something that happens in the real world, and which happens, even within the real world, more frequently in areas of poor governance and open conflict; and those are areas in which there are important moral actions, and important moral consequences, so they are areas in which it is interesting to set games.
|
||||
|
||||
It would be ludicrous to argue 'sexual violence is wrong, therefore we should not represent it in games.' Killing people is also wrong, yet it is extremely common in games. However, sexual violence — and in particular the representation of sexual violence — does pose some specific problems that need to be addressed.
|
||||
|
||||
Firstly, sexual violence is extremely gendered. Yes, male people are sometimes subjected to sexual violence, but nevertheless the overwhelming majority of victims of sexual violence are female. Yes, female people are sometimes — extraordinarily rarely, but sometimes — perpetrators of sexual violence, but nevertheless perpetrators of sexual violence are almost exclusively male.
|
||||
|
||||
Secondly, it is extremely tricky to represent sexual violence in visual media without eroticising it. There's a [very famous scene in Last Tango in Paris](https://www.independent.co.uk/arts-entertainment/films/news/last-tango-in-paris-butter-scene-b2270513.html) which the director might claim is consented in context, but which appears to me to be a clear case of anal rape, which is nevertheless highly erotic. There's a scene in [High Plains Drifter](https://en.wikipedia.org/wiki/High_Plains_Drifter#Plot) where no part of the rape is actually represented — it happens off screen — but it is nevertheless perceived by many people (including me) as eroticised. Many people — I suspect more men than women, but certainly including many women — do find the idea of rape erotic. It seems to me highly undesirable that a game should be seen to be rewarding immoral action.
|
||||
|
||||
(Yes, I know many modern games do quite explicitly reward killing, including of characters whose culpability is at best trivial, but — surely — this is something we should be seeking to move away from.)
|
||||
|
||||
## Subtlety and Nuance
|
||||
|
||||
A final issue here is that sexual interactions between people are subtle, and are subtle even around issues of consent. A less powerful person (normally a woman) — alone or as a member of a weak party, a party of perhaps older people, other women, children — may submit to sex with more powerful others without protest in order to protect others in their party, or to avoid death or serious injury, or to avoid starvation, or to escape debt. Do any of these things truly count as consent?
|
||||
|
||||
Again, a less powerful person may submit to sex with more powerful others transactionally in return to protection, or shelter, or food, or other resources. In modern society we might see this as sex work, and we might argue that sex work falls into the same moral category as any other labour entered into transactionally. But, generally, is it moral that people should be put into a position where their survival depends on their ability to sell any sort of unwilling labour?
|
||||
|
||||
(This is not to deny that some people, who do have secure living conditions or who could choose to do other things in order to gain secure living conditions do choose, willingly and voluntarily, to engage in sex work; and it isn't to criticise those people in any way).
|
||||
|
||||
Games are not very good at subtly and nuance. When, while playing a game, the character who is our avatar in the game, who we thought we were controlling, does something which we didn't intend them to do, it's very wrenching and immersion-breaking.
|
||||
|
||||
At the same time, if other characters in the game interpret something the player's character has done as sexual violence when the player did not intend sexual violence, that's also undesirable.
|
||||
|
||||
So, questions:
|
||||
|
||||
## Sex between non-player characters
|
||||
|
||||
People have sex. If people didn't have sex, there wouldn't be people; but more, if people didn't have sex, there wouldn't be (many) stories, since most stories are driven at least in part by sex. So pretending that non-player characters don't have sex is worse than unrealistic.
|
||||
|
||||
We live in a pathologically repressed society, in which open sex — sex in public places, sex with other people present — is rare, is seen as deviant, is (perhaps in consequence) highly eroticised. Does that mean that all the societies we represent in our games must be similarly repressed?
|
||||
|
||||
I would argue strongly to the contrary. Games are environments in which we can explore moral possibilities, and a society in which public sexuality was normal is clearly a possibility. Would such a society be a better society? Games are a mechanism through which we can ask that question, and questions of that sort.
|
||||
|
||||
If we're going to represent a society in which public sex is normal, then we're going to have to represent public sex on screen. It can take one of many forms:
|
||||
|
||||
1. Sex as normal activity — it's just going on in the background, and no other non-player characters pay much attention;
|
||||
2. Sex as conscious performance — sex where the participants intend to be watched, and other non-player characters do pay attention (this may include consciously eroticised performance);
|
||||
3. Sex as part of a religious or other ritual event — this is related to, and is, sex as conscious performance, but the purpose of the performance is symbolic and/or sacramental. This doesn't mean it is not eroticised, but it may not be eroticised.
|
||||
|
||||
By 'eroticised', I'm meaning deliberately intended to trigger sexual feelings in the audience — which, if the player character is present, includes the player.
|
||||
|
||||
## Sexual violence between non-player characters
|
||||
|
||||
In a world in which there are characters who are thuggish, who seek to dominate, terrorise and subdue other characters, whether those characters are outlaws or soldiers or aristocrats, to pretend that rape would not be used as a means to dominate, terrorise or subdue is… bowdlerisation. It's unrealistic, and it's a morally indefensible choice.
|
||||
|
||||
So there has to be a mechanism for non-player characters to decide to commit acts of sexual violence towards other non-player characters. The player must at least hear of such events through the gossip network, and should be able to find the specific non-player characters involved, and speak to them. Whether it's necessary to portray acts of sexual violence on screen is something I'm much less persuaded by, simply because it runs the risk of eroticising them.
|
||||
|
||||
## Mutually consented sexual activity between the player character and non-player characters
|
||||
|
||||
Mutually consented sexual behaviour between the player character and (certain, scripted) non-player characters has been a feature of video games for some time, and has occasionally been portrayed with real sensitivity and eroticism. Two cases I would point to are
|
||||
|
||||
1. The sex scene between Geralt and Shani in The Witcher;
|
||||
2. The sex scene between V and Judy in Cyberpunk 2077.
|
||||
|
||||
Cyberpunk is a largely non-cutscene game, but the sex scenes is a cutscenes and I completely understand why, from a technical point of view: the player does not have, either with mouse and keyboard or with a game controller, nearly enough control over their character to convey the subtlety and nuance of a good sex scene. Sex scenes in most video games are also pretty rare, and that must be at least partly because of cultural prurience.
|
||||
|
||||
But if a game allows a player to have a long lasting, narratively sexual relationship with a non-player character, as many games do, then sex is a behaviour which may happen repeatedly, and just playing the same cutscene over and over again is not going to be an adequate way of representing that.
|
||||
|
||||
The ideal would be to have a moderately large library of brief motion captures of people authentically having sex, and to be able to select performances at random from that library to apply to the body models of the characters who in the game are having sex, whether that be the player character with a non-player character, or two non-player characters. In the case where the player character is involved, this would happen in the location where the player chose to initiate it, so it wouldn't be a cutscene in the normal sense; but I think that the controller should be disabled for the duration of the performance.
|
||||
|
||||
## Sexual violence by one non-player character towards another
|
||||
|
||||
This is at least implicitly represented in existing video games, and while caution about eroticising it should be maintained, I think it's something which should be narratively possible.
|
||||
|
||||
## Sexual violence from the player character towards non-player characters
|
||||
|
||||
This would be extremely tricky (and controversial!) to handle; I *think* it ought to be in the narrative toolkit, but I have no specification to offer just now.
|
||||
|
||||
## Sexual violence from a non-player character towards the player character
|
||||
|
||||
Even trickier!
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
**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.
|
||||
|
||||
**NOTE**: Work on this is being carried on in a separate library, [Walkmap](https://github.com/simon-brooke/walkmap), q.v.
|
||||
|
||||
## Stages in creating routes between locations
|
||||
|
||||
### The 'procedural' phase
|
||||
|
||||
*see also [[Baking-the-world]]*
|
||||
*see also [Baking the world](Baking-the-world.html)*
|
||||
|
||||
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.
|
||||
|
||||
|
@ -16,17 +16,55 @@ The algorithmic part of choosing a route is the same during this baking phase as
|
|||
|
||||
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
|
||||
### Routing
|
||||
|
||||
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;
|
||||
Routing is fundamentally by [A\*](https://www.redblobgames.com/pathfinding/a-star/introduction.html), I think.
|
||||
|
||||
#### 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, if any (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.
|
||||
|
||||
#### Step costing:
|
||||
|
||||
Step cost is something like:
|
||||
|
||||
(/
|
||||
(-
|
||||
(+ distance
|
||||
(expt height-gained height-gain-exponent)
|
||||
(reduce + (map crossing-penalty watercourses-crossed)))
|
||||
(reduce + (map bridge-bonus bridges-crossed)))
|
||||
(or road-bonus 1))
|
||||
|
||||
**Where**
|
||||
|
||||
* `distance` traversed is in metres;
|
||||
* `height-gained` is in metres;
|
||||
* `height-gain-exponent` is tunable;
|
||||
* river `crossing-penalty` varies with a (tunable) exponent of the flow;
|
||||
* `bridge-bonus` works as follows: bridge bonus for a bridge entirely cancels the river crossing penalty
|
||||
for the watercourse the bridge crosses; bridge bonus for a ferry cancels a (tunable) fraction the river
|
||||
crossing penalty.
|
||||
* road-bonus for a road is substantial - probably about 8; for a track is less than road but greater than footpath, say 5; for a footpath has to be at least 3, to provide an incentive to
|
||||
stick to paths. All these values are tunable. Road bonus ought also to increase a small amount with each traversal of the path segment, but that's still to be worked on.
|
||||
|
||||
A lot of this is subject to tuning once we have prototype code running.
|
||||
|
||||
Somewhere into all this I need to factor tolls charged by local aristons,
|
||||
especially for bridges/ferries, and risk factors of hostile action, whether
|
||||
by outlaws or by hostile factions. But actually, that is at a per actor
|
||||
level, rather than at a pathmaking level: richer actors are less deterred
|
||||
by tolls, better armed actors less deterred by threat of hostile action.
|
||||
|
||||
### 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.
|
||||
River crossings appear automatically when the number of traversals of a particular route across a watercourse passes some threshhold. The threshold probably varies with an exponent of the flow; the threshold at which a ferry will appear is lower (by half?) than the threshold for a bridge. Of course river crossings, like roads, can also be pre-designed by game designers.
|
||||
|
||||
Where a river is shallow enough, (i.e. where the flow 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
|
||||
|
||||
|
@ -65,17 +103,7 @@ I'm working on a separate library, [walkmap](https://simon-brooke.github.io/walk
|
|||
|
||||
### 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"
|
||||
Dealing with large heightmaps - doing anything at all with them - is extremely compute intensive.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -107,6 +135,10 @@ we can then collect contiguous groups of zones each having at least one holding,
|
|||
|
||||
At least one of phases three, four, five and six is probably redundant; but without trying I'm not sure which.
|
||||
|
||||
#### Relevant actor classes by phase
|
||||
|
||||
Craftspeople and primary producers do travel between settlements, but only exceptionally. They mainly travel within at most a few kilometres of home; so they are primarily relevant in phases four and five, and need not be activated during phase six. Similarly, merchants primarily travel between settlements, and rarely within settlements; therefore, they need not be activated in phase four, and probably not even in phase five; but they must do a lot of journeys - substantially their full repertoire - in phase six.
|
||||
|
||||
### 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.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#### 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)*
|
||||
*(You might want to read this essay in conjunction with my older essay, [Settling a game world](Settling-a-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.
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
|||
|
||||
|
||||
|
||||
| Occupation | Dwelling | condition | New trade | Notes |
|
||||
| Occupation | Dwelling | Condition | New trade | Notes |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Vagrant | None | land available and animals available | Herdsman | |
|
||||
| Vagrant | None | arable land available | Farmer | See crops |
|
||||
|
@ -43,70 +43,81 @@
|
|||
|
||||
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.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th rowspan="3"> Craft <th rowspan="3"> Dwelling <th rowspan="3"> Supplies <th rowspan="3"> Perishable? <th rowspan="3"> Customer types <th rowspan="3"> Needs market? <th colspan="6"> Customers <th rowspan="3"> Supplier <th rowspan="3"> Suppliers <th rowspan="3"> Recruits </th></tr>
|
||||
<tr><th colspan="2"> Solo <th colspan="2"> Per journeyman <th colspan="2"> Per apprentice</tr>
|
||||
<tr><th> Min <th> Max <th> Min <th> Max <th> Min <th> Max </tr>
|
||||
<tr><td>Smith <td>Forge <td>Metal Items <td>no <td>Farmer, Soldier <td>No <td>6 <td>10 <td>4 <td>6 <td>1 <td>3 <td>Miner <td>1 <td>Vagrant
|
||||
<tr><td>Baker <td>Bakery <td>Bread <td>yes <td>All NPCs <td>No <td>20 <td>30 <td>12 <td>18 <td>6 <td>10 <td>Miller <td>1 <td>Vagrant
|
||||
<tr><td>Miller <td>Mill <td>Flour, meal <td>no <td>Baker, Innkeeper <td>No <td>2 <td>3 <td>1 <td>2 <td>1 <td>1 <td>Farmer <td>6 <td>Vagrant
|
||||
<tr><td>Weaver <td>Weaver's house <td>Cloth <td>no <td>All NPCs <td>Yes <td>6 <td>10 <td>4 <td>6 <td>1 <td>3 <td>Herdsman <td>2 <td>Vagrant
|
||||
<tr><td>Innkeeper <td>Inn <td>Food, hospitality <td>yes <td>Merhant, Soldier, Farmer, Lord <td>No <td>10 <td>20 <td>5 <td>10 <td>2 <td>4 <td>Farmer,Herdsman <td>2 <td>Vagrant
|
||||
<tr><td>Miner <td>Mine <td>Ores <td>no <td>Smith <td>Yes <td>2 <td>3 <td>1 <td>2 <td>1 <td>1 <td>Farmer <td>1 <td>Vagrant
|
||||
<tr><td>Butcher <td>Butchery <td>Meat <td>yes <td>All NPCs <td>No <td>10 <td>20 <td>4 <td>8 <td>2 <td>4 <td>Farmer, Herdsman <td>2 <td>Vagrant
|
||||
<tr><td>Merchant <td>Townhouse <td>Transport, logistics <td>n/a <td>Craftsmen, nobility <td>Yes <td>10 <td>20 <td>4 <td>8 <td>2 <td>4 <td>n/a <td>n/a <td>Vagrant
|
||||
<tr><td>Banker <td>Bank <td>Financial services <td>yes <td>Merchant <td>Yes <td>10 <td>20 <td>4 <td>8 <td>2 <td>4 <td>n/a <td>n/a <td>Merchant
|
||||
<tr><td>Scholar <td>Academy <td>Knowledge <td>n/a <td>Ariston, Tyrranos, General, Banker <td>No <td>1 <td>4 <td>1 <td>2 <td>0.25 <td>0.5 <td>n/a <td>n/a <td>Vagrant
|
||||
<tr><td>Priest <td>Temple <td>Religion <td>n/a <td>All NPCs <td>No <td>50 <td>100 <td> <td> <td> <td> <td> <td> <td>Scholar
|
||||
<tr><td>Chancellor <td>Chancellory <td>Administration <td>n/a <td>Ariston, Tyrranos <td>No <td>1 <td>1 <td>0 <td>0 <td>0 <td>0 <td> <td> <td>Scholar
|
||||
<tr><td>Lawyer <td>Townhouse <td>Legal services <td>n/a <td>Ariston, Merchant, Banker <td>No <td>4 <td>6 <td>2 <td>3 <td>1 <td>2 <td> <td> <td>Scholar
|
||||
<tr><td>Magus <td>Townhouse <td>Magic <td>n/a <td>Tyrranos, General <td>No <td>3 <td>4 <td>1 <td>2 <td>0.25 <td>0.5 <td> <td> <td>Scholar
|
||||
</table>
|
||||
|
||||
| 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 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.
|
||||
### Related crafts
|
||||
|
||||
It's assumed that a journeyman scholar, presented with the opportunity, would prefer to become an apprentice magus than a master scholar.
|
||||
There are groups of crafts which should probably be seen as related crafts, where apprenticeship in one should serve as qualification to serve as journeyman in another. For example, there is a family of woodworking crafts, whose base is probably `Joiner`.
|
||||
|
||||
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.
|
||||
Crafts within this family include
|
||||
|
||||
The list of crafts given here is illustrative, not necessarily exhaustive.
|
||||
* Boatwright
|
||||
* Cabinetmaker
|
||||
* Cartwright
|
||||
* Cooper
|
||||
* Lutanist
|
||||
* Military Artificer
|
||||
* Millwright
|
||||
* Turner
|
||||
|
||||
So although I think these are separate crafts, all are Joiners; all can undertake construction joinery work; and a journeyman who has served as apprentice to any can serve as journeyman to any other. Since journeymen will typically serve under more than one master before settling down, it will be possible for one person to have served under masters in two different related trades and therefore be qualified to set up as a master of either.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
<table>
|
||||
<tr><th rowspan="2"> Rank <th rowspan="2"> Follower rank <th rowspan="2"> Client type <th colspan="2"> Clients protected <th colspan="2"> Trade in market <th colspan="2"> Followers per client
|
||||
<tr><th> Min <th> Max <th> Min <th> Max <th> Min <th> Max
|
||||
<tr><td>Bonnet Laird <td>Private <td>Farmer <td>6 <td>20 <td>0 <td>100 <td>0.25 <td>0.5
|
||||
<tr><td>Ariston <td>Captain <td>Bonnet Laird <td>10 <td>30 <td>25 <td>1000 <td>0.5 <td>1
|
||||
<tr><td>Tyrranos <td>General <td>Ariston <td>10 <td>unlimited <td>250 <td>unlimited <td>0.1 <td>0.5
|
||||
</table>
|
||||
|
||||
| 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.
|
||||
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
<table>
|
||||
<tr><th rowspan="2"> Rank <th rowspan="2"> Follower rank <th colspan="2"> Followers <th rowspan="2"> Condition <th rowspan="2"> New rank
|
||||
<tr><th> Min <th> Max
|
||||
<tr><td>Private <td>None <td>0 <td>0 <td>Battle hardened, unled privates <td>Sergeant
|
||||
<tr><td>Sergeant <td>Private <td>5 <td>15 <td>More battle hardened, unled sergeantts <td>Captain
|
||||
<tr><td>Captain <td>Sergeant <td>5 <td>15 <td>More battle hardened, unled captains <td>General
|
||||
<tr><td>General <td>Captain <td>5 <td>unlimited <td> <td>
|
||||
</table>
|
||||
|
||||
| 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.
|
||||
Soldiers have no loyalty to their employer's employer.
|
||||
|
|
30
doc/Roadmap.md
Normal file
30
doc/Roadmap.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Roadmap (obsolete)
|
||||
|
||||
This document outlines a plan to move forward from where I am in June 2021.
|
||||
|
||||
**NOTE**: this document has been [superceded](MVP-Roadmap.html).
|
||||
|
||||
# JMonkeyEngine
|
||||
|
||||
[JMonkeyEngine](https://jmonkeyengine.org/) is not, at this time, an AAA game engine. But at the same time I'm never, really, going to build an AAA game. It is a working game engine which can display characters on screen in scenery and have them move around, and, actually, they can be fairly sophisticated. It will be resaonably easy to integrate Clojure code with JMonkeyEngine - easier than it would be to integrate either Clojure or Common Lisp with [Unreal Engine](https://www.unrealengine.com/) or [Unity 3D](https://unity.com/). As a significant added bonus, JMonkeyEngine is open source.
|
||||
|
||||
Consequently I plan to stop agonising about what game engine to use, and seriously focus on getting something working in JMonkeyEngine.
|
||||
|
||||
# Not Reinventing Wheels
|
||||
|
||||
JMonkeyEngine already has working code for walking animated characters, which is entirely adequate to proof-of-concept what I want to do. Rather than try to implement them myself, I just intend to use existing JMonkeyEngine code as far as possible.
|
||||
|
||||
# The 1Km World
|
||||
|
||||
I propose to build a 1Km square world, containing one settlement, as a proof of concept for
|
||||
|
||||
1. Procedural (genetic) buildings;
|
||||
2. Procedural settlement planning;
|
||||
3. Procedural characters, probably based on [MakeHuman 'Mass Produce' plugin](https://youtu.be/jRHnJX-TdT4), using walk animation based on [TestWalkingChar](https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java);
|
||||
4. Characters with their own hierarchy of needs, and their own means of planning to fulfil these;
|
||||
5. Characters with individualised knowledge about the world;
|
||||
6. Characters who can parse typed questions, and produce either a textual or audio response;
|
||||
7. Characters with procedurally generated accents (very stretch goal)!
|
||||
8. Characters who can listen to spoken questions, and produce audio responses.
|
||||
|
||||
At that stage, I have a technology demonstrator that will be interesting. It still leaves the big procedural world builder still to do, but it would be enough technology to get other people interested in the project.
|
|
@ -35,7 +35,7 @@ The next tier of playable roles rotates around issues arising from the mercantil
|
|||
|
||||
### Aristocracy
|
||||
|
||||
Aristocrats are basically settled outlaws who seek to establish a monopoly on extracting taxes from inhabitants and travellers in a particular region by driving out all other outlaws. Within the comain of an aristocrat, you have to pay tax but you're reasonably safe from being attacked by other outlaws and losing everything. Aristocrats may also maintain and improve roads and bridges and do other things to boost the economy of their territory, may expant into adjoining territory with no current aristocratic control, and may wage war on other aristocrats.
|
||||
Aristocrats are basically settled outlaws who seek to establish a monopoly on extracting taxes from inhabitants and travellers in a particular region by driving out all other outlaws. Within the domain of an aristocrat, you have to pay tax but you're reasonably safe from being attacked by other outlaws and losing everything. Aristocrats may also maintain and improve roads and bridges and do other things to boost the economy of their territory, may expand into adjoining territory with no current aristocratic control, and may wage war on other aristocrats.
|
||||
|
||||
An outlaw ought to be able to become an aristocrat, by dominating an ungoverned area or by defeating an existing aristocrat.
|
||||
|
||||
|
@ -45,22 +45,24 @@ Soldiers, like aristocrats, are basically on the same spectrum as outlaws. Outla
|
|||
|
||||
## Routine, Discretion and Playability
|
||||
|
||||
There's a term that's used in criticism of many computer games which is worth thinking about hard here: that term is 'farming'. 'Farming', in this sense, is doing something repetitive and dull to earn credits in a game. Generally this is not fun. What makes roles in a game-world fun is having individual discretion - the ability to choose between actions and strategies - and a lack of routine.
|
||||
There's a term that's used in criticism of many computer games which is worth thinking about hard here: that term is 'farming'. 'Farming', in this sense, is doing something repetitive and dull to earn credits in a game. Generally this is not fun. What makes roles in a game-world fun is having individual discretion — the ability to choose between actions and strategies — and a lack of routine.
|
||||
|
||||
Most craft skills - especially in the learning phase - are not like this, and crafts which are sophisticated enough to be actually engaging are very hard to model in a game. Learning a craft is essentially, inherently, repetitive and dull, and if you take that repetition out of it you probably don't have enough left to yield the feeling of mastery which would reward success; so it doesn't seem to me that making craft roles playable should be a priority.
|
||||
Most craft skills — especially in the learning phase — are not like this, and crafts which are sophisticated enough to be actually engaging are very hard to model in a game. Learning a craft is essentially, inherently, repetitive and dull, and if you take that repetition out of it you probably don't have enough left to yield the feeling of mastery which would reward success; so it doesn't seem to me that making craft roles playable should be a priority.
|
||||
|
||||
## Cruise control
|
||||
|
||||
One of the most enjoyable aspects of The Witcher 3 - still my go-to game for ideas I want to improve on - is simply travelling through the world. Although fast travel is possible I find I rarely use it, and a journey which takes fifteen minutes of real world wall clock time can be enjoyable in and of itself. This is, of course, a credit to the beautiful way the world is realised.
|
||||
One of the most enjoyable aspects of The Witcher 3 — still my go-to game for ideas I want to improve on — is simply travelling through the world. Although fast travel is possible I find I rarely use it, and a journey which takes fifteen minutes of real world wall clock time can be enjoyable in and of itself. This is, of course, a credit to the beautiful way the world is realised.
|
||||
|
||||
But nevertheless, in The Witcher 3, a decision was made to pack incident fairly densely - because players would find just travelling boring. This leads to a situation where peaceful villages exist two minutes travel from dangerous monsters or bandit camps, and the suspension of disbelief gets a little strained. Building a world big enough that a market simulation is believable means that for the individual, the travel time to a market where a particular desired good is likely to be cheaper becomes costly in itself. Otherwise, there's no arbitrage between markets and no ecological niche for a merchant to fill. The journey time from market to market has to be several in-game days.
|
||||
(It's worth noting that [Kenshi](https://lofigames.com/), a game I'm coming to greatly admire, does not allow fast travel at all, but has an equivalent of 'cruise control' — you can set a destination and then accelerate time and simply watch as your characters journey).
|
||||
|
||||
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.
|
||||
But nevertheless, in The Witcher 3, a decision was made to pack incident fairly densely — because players would find just travelling boring. This leads to a situation where peaceful villages exist two minutes travel from dangerous monsters or bandit camps, and the suspension of disbelief gets a little strained. Building a world big enough that a market simulation is believable means that for the individual, the travel time to a market where a particular desired good is likely to be cheaper becomes costly in itself. Otherwise, there's no arbitrage between markets and no ecological niche for a merchant to fill. The journey time from market to market has to be several in-game days.
|
||||
|
||||
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.
|
||||
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 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 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.
|
||||
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.
|
||||
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.
|
70
doc/Selecting_Character.md
Normal file
70
doc/Selecting_Character.md
Normal file
|
@ -0,0 +1,70 @@
|
|||
# Selecting the Player Character
|
||||
|
||||
## Background
|
||||
|
||||
Many computer role playing games, particularly older ones such as Neverwinter Nights, allow you to 'design' your player character from a fairly broad canvas. Race, class, attributes, gender and appearance are all selectable.
|
||||
|
||||
Choice has eroded over time. For example the Dragon Age series, where you can chose between three races, two genders, and a small number of classes. In the Mass Effect trilogy, you play as Shepard, who is human and essentially a Fighter, but can be either male or female and whose appearance you can customise. You can play as either lawful good or chaotic neutral. In Cyberpunk 2077, you play as V, who is human, either male or female, essentially a Fighter, and chaotic neutral.
|
||||
|
||||
In more recent games, there has been a trend towards more limited choice. In the games of The Witcher series, you get no choice at all, but play as Geralt of Rivia, who in the categorisation of Dungeons and Dragons, is a Fighter/Ranger, male, human, and somewhere between chaotic good and chaotic neutral depending on how you play him. In the Horizon series, you play as Aloy, again a Fighter/Ranger, female, human, and essentially chaotic good.
|
||||
|
||||
As I've argued elsewhere, part of the reason for limiting choice is voice acting.
|
||||
|
||||
Limiting choice of player character, especially in games with increasingly highly scripted stories, limits replayability; after two or three playthroughs, there are very few interesting surprises left.
|
||||
|
||||
## The Self-voiced Player
|
||||
|
||||
If we have voice interaction sufficiently sophisticated that we can allow the player character to say more or less whatever they want to say — [and my argument here is that we can do this](Gossip_scripted_plot_and_Johnny_Silverhand.md) — then we don't need voice acting for the player character, and that gives us a lot of freedom. There's then really no reason why the player can't inhabit any character in the game world and play as that character.
|
||||
|
||||
## Tinder as a Character Selector
|
||||
|
||||
Tinder is a dating app. It shows you pictures (with brief profiles) of potential partners, and you choose from them by swiping left to reject them, or right to express interest in them. That's a kernel of an idea for how to select from among a large selection of people.
|
||||
|
||||
So how about:
|
||||
|
||||
1. The game developer selects a large subset of characters in the game as potentially playable. This might be as large as all characters who are not plot characters, as small as only soldiers, or most plausibly, any adult who is not yet in a long term romantic relationship. This forms the candidate set.
|
||||
2. In the character selector, the game shows a character chosen at random from the set, and, each time the player rejects the character shown, shows another until the player accepts a character.
|
||||
|
||||
That works, but we can do better.
|
||||
|
||||
## Refining the Selection
|
||||
|
||||
Suppose, below the image of the character on the selection screen, we have a short text caption with name, age, home, occupation, gender, and below that, we have a row of icons showing attributes, with some representation of the character's relative measurement of that attribute. Clicking one of these attribute icons would be interpreted as meaning 'show me a character quite like the current character, but having a higher score on this particular attribute'.
|
||||
|
||||
### Refinable Attributes
|
||||
|
||||
In lots of games which present the player with dialogue options, there are some options which can't be selected unless the player character passes a 'skill check'. Very often, for example, a player won't be able to issue a particular threat unless they have a specific value of strength, or to say something flirtatious unless they have a specific value of charm.
|
||||
|
||||
It makes no sense in a game in which the player gets to freely choose what to say for an attribute like 'charm' to be a refinable attribute. Instead, responding to 'charming' or flirtatious or threatening or funny or sexually suggestive speech is a matter for the programming of a particular non-player character (although the interpretation of the speech and the tagging of it as charming or flirtatious or threatening or funny or suggestive would be a function of the top level speech input processor).
|
||||
|
||||
So, sensibly refinable attributes might include things like
|
||||
|
||||
1. Strength;
|
||||
2. Agility;
|
||||
3. Dexterity;
|
||||
4. Endurance.
|
||||
|
||||
I did think that 'intelligence' or 'learning' might be on that list but the more I think of it, the harder I find it to understand how low intelligence might be represented in a game in which the player speaks freely.
|
||||
|
||||
There's another attribute icon with slightly different semantics which might sensibly be added, and that's gender. Selecting this icon would be interpreted as meaning 'show me a character quite like the current character, but having a different gender'.
|
||||
|
||||
### Summary design
|
||||
|
||||
So the character selecter now looks like
|
||||
|
||||
1. A main area in which a rendering of the proposed character is shown; this rendering can be zoomed and rotated, so that the player can look at the face and body from different angles;
|
||||
2. A description panel, normally hidden but when displayed replacing the character rendering in the main area, giving fuller biographical information about the character;
|
||||
3. Below the main area, a caption, giving name, age, gender, occupation, home;
|
||||
4. To the right hand side, a vertical column of attribute icons.
|
||||
|
||||
To interact with the screen, the player can
|
||||
|
||||
1. Zoom in and out on the rendered image, for example with a mouse scroll wheel or the left and right trigger buttons of a game controller;
|
||||
2. Rotate the rendered image, for example by dragging with the right mouse button held down or with the right joystick of a game controller;
|
||||
3. Toggle between the character render and the description panel;
|
||||
4. When the description panel is displayed, scroll it;
|
||||
5. Select any attribute icon to refine the choice of character;
|
||||
6. 'Swipe left' (or other action) to reject the current choice of character and choose another, without refining in any specific way;
|
||||
7. 'Swipe right' to select the current character and procede into the game.
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#### 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)')*
|
||||
*This essay is part of a series with '[Worlds and Flats](https://www.journeyman.cc/blog/posts-output/2008-04-04-worlds-and-flats/)' 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
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
|||
|
||||
### 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 objective of this essay is to outline an algorithm 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
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ OK? Let's start.
|
|||
|
||||
When a man and a woman have sex, there's a non-zero chance that the woman will get pregnant. There's a zero chance that the male will get pregnant.
|
||||
|
||||
A woman can typically give birth to of the order of twelve children in the course of her life, and each childbirth involves a non-zero risk of death. If modelling the sort of bronze-age-to-late-medieval cultures I'm generally considering, there are no available reliable methods of contraception, although their may be, for example, known spermicidal or abortifacient spells or potions. If it's abortifacient, that's pretty unpleasant for the woman, too.
|
||||
A woman can typically give birth to of the order of twelve children in the course of her life, and each childbirth involves a non-zero risk of death. If modelling the sort of bronze-age-to-late-medieval cultures I'm generally considering, there are no available reliable methods of contraception, although there may be, for example, known spermicidal or abortifacient spells or potions. If it's abortifacient, that's pretty unpleasant for the woman, too.
|
||||
|
||||
Children, especially when young, are very vulnerable and need protection. Children with good protection are much more likely to survive to adulthood. Raising children involves a fair amount of work.
|
||||
|
||||
|
@ -26,7 +26,7 @@ Another significant point is that women's ability to bear children ceases at a m
|
|||
|
||||
## Why have sex at all?
|
||||
|
||||
If a character has 'having children' - the [**Ancestor**](intro.html#aspirations-and-goals) aspiration, in my typology - as their key aim, then they will want to have sex. But to have children in this sense is to have acknowledged children, so while a male character may be motivated to have multiple female partners, he will never the less have some degree of long term committment to them, and will want both to feel confident that the children are his and to be recognised by their father.
|
||||
If a character has 'having children' — the [**Ancestor**](intro.html#aspirations-and-goals) aspiration, in my typology — as their key aim, then they will want to have sex. But to have children in this sense is to have acknowledged children, so while a male character may be motivated to have multiple female partners, he will never the less have some degree of long term committment to them, and will want both to feel confident that the children are his and to be recognised by their father.
|
||||
|
||||
From the point of view of seeking to become an Ancestor, there is little benefit to the woman in having multiple partners, except in very harsh environments. It will be easier to give one partner confidence that all your children are his, and while a man can increase his number of potential progeny by having multiple wives, mistresses or other classes of long-term female sexual partners, a woman cannot.
|
||||
|
||||
|
@ -42,4 +42,13 @@ Sex, done right, is an extremely pleasant pastime. Sex can also be used to creat
|
|||
|
||||
For women, sex with other women carries with it no risk of pregnancy, so can be enjoyed or used for any of these purposes in very much the same way as it can by men; in other words, particularly for women, homosexual sex can be more lighthearted and carefree than heterosexual sex. To what extend our notions of homosexuality and heterosexuality are cultural I simply don't know. But because no children will result, a woman can afford to be more promiscuous with other women than she can with men.
|
||||
|
||||
##
|
||||
## Women and warrior/adventurer lifestyles
|
||||
|
||||
Generally speaking, people do not want to take their children onto a battlefield. If you're going to have a game world in which women significantly take on warrior or adventurer roles, then you must either have
|
||||
|
||||
* A culture which expects female warriors to be celibate; or
|
||||
* A culture with access to and acceptance of safe and reliable contraception or abortion; or
|
||||
* An acceptance of leaving infant children with grandparents, other relatives or foster carers for long periods;
|
||||
* A system of long term creches;
|
||||
* Any combination of the above.
|
||||
|
|
@ -14,7 +14,7 @@ This is mainly a land game. Broadly, caravans take the place of ships in Elite o
|
|||
|
||||
## A political simulation
|
||||
|
||||
Broadly, aristons claim territories in an essentiallu feudal arrangement, drive out outlaws, and levy taxes.
|
||||
Broadly, aristons claim territories in an essentially feudal arrangement, drive out outlaws, and levy taxes.
|
||||
|
||||
An ariston will be popular if their regime is stable, if taxes are low, justice is considered fair, oppression is low and depredations by outlaws are minimal. The more unpopular an ariston is, the more resistant the populace will be to paying their taxes, meaning the more military force needs to be diverted to tax collection and the greater the oppression. Taxes are required to pay soldiers and to maintain high roads, bridges, markets and other infrastructure. Merchants will prefer to travel routes which are better policed and maintained, which means more merchants trading in your markets, which means more tax.
|
||||
|
||||
|
|
|
@ -2,18 +2,17 @@
|
|||
|
||||
#### Saturday, 26 April 2008
|
||||
|
||||

|
||||
|
||||
|
||||
### 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)._
|
||||
*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://www.journeyman.cc/blog/posts-output/2008-04-26-the-spread-of-knowledge-in-a-large-game-world/).*
|
||||
|
||||
-----
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
@ -65,11 +64,11 @@ But also, the added knowledge is *degraded*. If the recipient isn't from Hans'hu
|
|||
: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'.
|
||||
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
|
||||
* 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
|
||||
|
||||
|
@ -79,8 +78,8 @@ The timestamp could also be degraded, or lost altother - although how exactly th
|
|||
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
|
56
doc/Things_Voice_Interaction_Enables.md
Normal file
56
doc/Things_Voice_Interaction_Enables.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
# Things Voice Interaction Enables
|
||||
|
||||
## Organic quest routing
|
||||
|
||||
In a world in which you can talk to non-player characters, and in which non-player characters know the directions to things which are local to their homes (and some, travellers, will be able to give you routes to things further away), when you need to get to your next waypoint you can just ask for directions. That much is easy.
|
||||
|
||||
But something much richer occurred to me.
|
||||
|
||||
Suppose you're entering a village, and you meet a random character. That character knows any local quest giver, and what it is that quest giver needs –– and, indeed, they know this whether the quest is scripted or organic.
|
||||
|
||||
So the random character could say
|
||||
|
||||
> Hello, I'm Tobias, and that my mill over there. Who might you be, stranger?
|
||||
|
||||
At which point you can either tell him, or not. Suppose you tell him, he could say
|
||||
|
||||
> Oh! I've heard of you. It's said you're very handy with a sword.
|
||||
|
||||
And you can reply however you like, acknowledging, or being modest, or perhaps even denying (although from this line of dialogue if you deny he'll think you're being modest, for reasons see later). He can then say, taking our example from the 'abducted child' quest in [the Introduction](intro.html#dynamic-quests),
|
||||
|
||||
> Thing is, old granny Grizzel's granddaughter Esmerelda has been abducted by bandits, and we've done a whip-around for a reward for someone who can rescue the girl.
|
||||
|
||||
At which point you may reply that you'll do it, or be non-committal, or say you won't. If you say you will, he can say,
|
||||
|
||||
> Well, you should talk to granny Grizzel, she lives in the white house by the crossroads, half a mile that-a-way (pointing).
|
||||
|
||||
If you say you won't, he can say,
|
||||
|
||||
> It would be a virtuous act, the old lady is fair desperate. If you should change your mind, you should talk to her; she lives in the white house by the crossroads, half a mile that-a-way (pointing).
|
||||
|
||||
OK, but what if, in the game world, the player character is not good with a sword? Well, the 'abducted child' quest can be resolved by violence; but it can also be resolved by persuasion, or by sneakiness, or by bribery. So suppose the player isn't (in the game) good with a sword, but is good at negotiation. Then in the initial approach, Tobias could say
|
||||
|
||||
> Oh! I've heard of you. It's said you're very handy at persuasion... Thing is, old granny Grizzel's granddaughter Esmerelda has been abducted by bandits, and we've done a whip-around for a ransom, but she's lacking someone who can negotiate for her.
|
||||
|
||||
It's the same quest, and, whatever Tobias has said, the player can still use either violence or persuasion or trickery to complete the quest (and gain appropriate reputation thereby), but it's flexible enough to adapt to the player's in-game persona, and it means we can direct the player to quest-givers without having to stick a bloody great icon on the quest giver's head.
|
||||
|
||||
So, to repeat for clarity: the idea is, if there is a quest in the vicinity, whether organic or scripted, many of the quest giver's neighbours may know about it, and will bring it up in conversation, introducing it and directing the player to the quest giver. And I believe that this can be done reasonably naturally.
|
||||
|
||||
Obviously there are some sorts of quests where for narrative reasons he quest giver will **not** want their neighbours to know of it, and those quests need to be signposted differently; but I think that effective ways need to be found of signposting those quests to the player without resorting to noticeboards or quest icons.
|
||||
|
||||
## Command in Battles
|
||||
|
||||
Player characters in role playing games are often narratively great heroic leaders — see any of the Dragon Age games but particularly Inquisition for examples of this — but when it comes to a pitched battle all they can do is follow a scripted battle plan and fight individual actions, because in current generation role-playing games there is no effective user interface to allow strategic and tactical control of a battle.
|
||||
|
||||
So how would a real-world, before modern communications technology, war leader command a battle? Why, by observing the battle and talking to people, and those are both things that in our game the player can do.
|
||||
|
||||
So, there are two stages to battle communication: the first is the council of war, before the battle, in which the battle plan is agreed. For the non-player characters to have any significant input into this, we'd need a really good knowledge base of appropriate battle strategies with heuristics for which plan fits which sort of geography and which sort of enemy, but that could be quite fun to develop; but in principle it's sufficient for the player character to be able to say to each of the divisional captains "I want you to do this," and for each captain to say first "yes, I understand" (or "please clarify"), and then "yes, I will do it" (or "yes, I will try").
|
||||
|
||||
No battle plan, of course, survives first contact with the enemy. It must be possible to update the plan during the battle, and messengers were used to carry new orders from the commander to subordinates. That, of course, we can also do.
|
||||
|
||||
So, ideally (and in describing this I'll try to give 'less than ideal' alternatives where I see them), you can gather your captains to a council of war, either by speaking to them directly or by sending messengers round. At the council of war, non-player-character captains can suggest possible battle plans drawn from a common knowledge base, but can have individual levels of boldness or caution. However, if you've been appointed battle leader, then provided they're individually still loyal to you then they will ultimately agree to what you order.
|
||||
|
||||
When battle is joined you can either join in the fighting in the front line in which case your strategic overview is going to be very limited and you'll just have to hope your initial plan was good enough; or else you can sit on a hilltop overlooking the battlefield with your trumpeter and your messengers, and send messages to control the fight, but not actually take part much yourself (unless everything really goes to shit and your position is overrun).
|
||||
|
||||
In real world battles orders were often misunderstood; I don't think I should do anything special to model that. But orders (other than trumpet calls) will necessarily take finite time, and if the battlefront is really messed up messengers may fail to get through.
|
||||
|
|
@ -5,9 +5,18 @@
|
|||

|
||||
|
||||
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.
|
||||
|
||||
> 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.
|
||||
|
||||
|
|
31
doc/intro.md
31
doc/intro.md
|
@ -33,21 +33,21 @@ repertoire of speech.
|
|||
## Previous essays that are relevant
|
||||
|
||||
* [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;
|
||||
* [Settling a game world](Settling-a-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://www.journeyman.cc/blog/posts-output/2013-07-04-tessellated-multilayer-height-map/) (2013) gives ideas for how a designed geography for a very large world could be stored relatively economically;
|
||||
* [Genetic Buildings](Genetic-buildings.html) (2013) sketches algorithms which would allow procedurally-generated buildings to be site-appropriate, broadly variable and reproducable;
|
||||
* [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](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;
|
||||
* [Modelling the change from rural to urban](https://www.journeyman.cc/blog/posts-output/2013-07-17-modelling-the-change-from-rural-to-urban/) (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](https://www.journeyman.cc/blog/posts-output/2013-10-01-of-pigeons-and-long-distance-messaging-in-a-game-world/) (2013) builds on ideas about flows of information;
|
||||
* [Modelling rural to urban, take two](https://www.journeyman.cc/blog/posts-output/2013-10-14-modelling-rural-to-urban-take-two/) (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://www.journeyman.cc/blog/posts-output/2014-09-28-more-on-modelling-rivers/) (2014) talks about modelling hydrology, with implications for soil fertility;
|
||||
* [Modelling settlement with cellular automata](https://www.journeyman.cc/blog/posts-output/2014-08-26-modelling-settlement-with-a-cellular-automaton/) (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](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](Baking-the-world.html) (2019) an outline of the overall process of creating a world.
|
||||
|
||||
## Organic and emergent game-play
|
||||
|
||||
If a world is [dynamically populated](https://blog.journeyman.cc/2014/08/modelling-settlement-with-cellular.html), with [dynamic allocation of livelihoods](https://blog.journeyman.cc/2013/07/populating-game-world.html) then several
|
||||
If a world is [dynamically populated](https://www.journeyman.cc/blog/posts-output/2014-08-26-modelling-settlement-with-a-cellular-automaton/), with [dynamic allocation of livelihoods](Populating-a-game-world.html) then several
|
||||
aspects of gameplay will emerge organically. First, of course, is just
|
||||
exploring; in a dynamically changing world there will always be more to
|
||||
explore, and it will be different in each restart of the game.
|
||||
|
@ -119,7 +119,7 @@ To make dynamic quests work, of course, you need a dynamic world; a world in
|
|||
which conflicts can arise. A world in which traders trade, robbers rob, lovers
|
||||
love, haters hate, scandal-mongers make scandal, organically and dynamically
|
||||
whether the player is there or not, and where news of these events will filter
|
||||
through to the player through the [gossip network](https://blog.journeyman.cc/2008/04/the-spread-of-knowledge-in-large-game.html) also organically and dynamically.
|
||||
through to the player through the [gossip network](The-spread-of-knowledge-in-a-large-game-world.html) also organically and dynamically.
|
||||
|
||||
## Extending the story
|
||||
|
||||
|
@ -177,7 +177,7 @@ easy:
|
|||
2. **Master**: what is the sum of (or average of) the esteem held for this agent by other agents of the same craft?
|
||||
3. **Explorer**: (e.g.) what is the sum of the distance between the most northerly and most southerly, and the most easterly and most westerly, locations this agent has visited?
|
||||
4. **Climber**: how many 'promotions' has this agent had in the game?
|
||||
6. **Conqueror**: how many total vassales, recursively, has this agent?
|
||||
5. **Conqueror**: how many total vassals, recursively, has this agent?
|
||||
6. **Citizen**: really, really tricky. Probably what is the average esteem for this agent among all agents within a specified radius? - although this will score more highly for agents who have taken part in notable events, and what I'm really thinking of for my ideal 'good citizen' is someone who really hasn't.
|
||||
|
||||
So each agent is assigned - by the dreaded random number generator - one top
|
||||
|
@ -323,13 +323,16 @@ A caravan or ship costs so much per day to run, irrespective of whether full
|
|||
or empty. So the base cost of a journey is a function of the time taken, which
|
||||
is essentially a function of the distance.
|
||||
|
||||
Obviously, on top of the base cost of movement there are tolls, which are imposed
|
||||
by the aristons through whose territory the journey passes (and therefore predictable, and can be used in route planning), and also the risk of having to bribe or fight outlaws, and the possible need to hire mercenaries to defend against outlaws, which is not predictable but can be estimated and thus also used in route planning.
|
||||
|
||||
### Outlawry and merchants
|
||||
|
||||
Outside the domains of aristons, outlaws may intercept caravans; when this
|
||||
happens the following outcomes are possible:
|
||||
|
||||
1. The merchant (together with any mercenaries the merchant has hired to protect the caravan) successfully fights off the outlaws;
|
||||
2. The outlaws steal the entire cargo (and may kill the merchant);
|
||||
2. The outlaws steal the entire cargo (and may kill the merchant and others);
|
||||
3. The merchant pays protection money to the outlaws, typically around 5%-10% of the value of cargo carried;
|
||||
4. The merchant employs the outlaws as caravan guards (see below);
|
||||
5. The outlaws allow the caravan to pass unmolested;
|
||||
|
@ -362,7 +365,7 @@ Generally, if a merchant buys goods in an ariston's market, or sells goods
|
|||
in the ariston's market, then the economy benefits and the ariston benefits
|
||||
from that; so the 'tax' element is part of the market markup. But if a
|
||||
caravan passes through an ariston's territory without stopping at a market,
|
||||
there's probably a tax of about 5% of value.
|
||||
there's probably a toll of about 5% of value.
|
||||
|
||||
Generally, an ariston's army will control outlawry within the ariston's
|
||||
domain, so outlaw encounters within a domain are unlikely. Soldiers could
|
||||
|
|
143
docs/cloverage/cc/journeyman/the_great_game/agent/agent.clj.html
Normal file
143
docs/cloverage/cc/journeyman/the_great_game/agent/agent.clj.html
Normal file
|
@ -0,0 +1,143 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/agent/agent.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.agent.agent
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Anything in the game world with agency; primarily but not exclusively
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 characters."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 (:require [cc.journeyman.the-great-game.objects.game-object :refer [ProtoObject]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [cc.journeyman.the-great-game.objects.container :refer [ProtoContainer]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
006
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ;;; hierarchy of needs probably gets implemented here
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ;;; I'm probably going to want to defprotocol stuff, to define the hierarchy
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 ;;; of things in the gameworld; either that or drop to Java, wich I'd rather not do.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
011 (defprotocol ProtoAgent
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 "An object which can act in the world"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 (act
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 [actor world circle]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 "Allow `actor` to do something in this `world`, in the context of this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 `circle`; return the new state of the actor if something was done, `nil`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 if nothing was done. Circle is expected to be one of
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
018
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 * `:active` - actors within visual/audible range of the player
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 character;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 * `:pending` - actors not in the active circle, but sufficiently close
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 to it that they may enter the active circle within a short period;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 * `:background` - actors who are active in the background in order to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 handle trade, news, et cetera;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 * `other` - actors who are not members of any other circle, although
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 I'm not clear whether it would ever be appropriate to invoke an
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 `act` method on them.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
028
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 The `act` method *must not* have side effects; it must *only* return a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 new state. If the actor's intention is to seek to change the state of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 something else in the game world, it must add a representation of that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 intention to the sequence which will be returned by its
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 `pending-intentions` method.")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 (pending-intentions
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 [actor]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 "Returns a sequence of effects an actor intends, as a consequence of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 acting. The encoding of these is not yet defined."))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
038
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 (defrecord Agent
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 ;; "A default agent."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 [name craft home culture]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 ProtoObject
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 ProtoContainer
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 ProtoAgent
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 )
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,251 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/buildings/module.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.buildings.module
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
002
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 "A module of a building; essentially something like a portacabin, which can be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 assembled together with other modules to make a complete building.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 Modules need to include
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 1. Ground floor modules, having external doors;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 2. Craft modules -- workshops -- which will normally be ground floor (except
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 weavers) and may have the constraint that no upper floor module can cover them;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 3. Upper floor modules, having NO external doors (but linking internal doors);
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 4. Roof modules
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 **Role** must be one of:
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 1. `:primary` a ground floor main entrance module
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 2. `:secondary` a module which can be upper or ground floor
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 3. `:upper` a module which can only be on an upper floor, for example one
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 with a projecting gallery, balcony or overhang.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 Other values for `role` will emerge.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 **Exits** must be a sequence of keywords taken from the following list:
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 1. `:left` an exit in the centre of the left wall
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 2. `:left-front` an exit in the centre of the left half of the front wall
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 3. `:front` an exit in the centre of the front wall
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 4. `:right-front` an exit in the centre of the right half of the front wall
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 5. `:right` an exit in the centre of the right wall
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 6. `:right-back` an exit in the centre of the right half of the back wall
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 7. `:left-back` an exit in the centre of the back wall
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 A module placed on an upper floor must have no exit which opens beyond the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 footprint of the floor below - no doors into mid air! However, it is allowable
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 (and indeed is necessary) to allow doors into roof spaces if the adjacent
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 module on the same floor does not yet exist, since otherwise it would be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 impossible to access a new room which might later be built there.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 **Load** must be a small integer indicating both the weight of the module and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 the total amount of weight it can support. So for example a stone-built module
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 might have a `load` value of 4, a brick built one of 3, and a half-timbered one
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 of 2, and a tent of 0. This means a stone ground floor module could support one
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 further floor of stone or brick, or two further floors of half timbered
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 construction; while a brick built ground floor could support a single brick or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 half-timbered upper floor but not a stone one, and a half-timbered ground floor
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 could only support a half timbered upper floor.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 There also needs to be an undercroft or platform module, such that the area of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 the top of the platform is identical with the footprint of the building, and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 the altitude of the top of the platform is equal to the altitude of the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 terrain at the heighest corner of the building; so that the actual
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 building doesn't float in the air, and also so that none of the doors or windows
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 are partly underground.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
054
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 Each module needs to wrap an actual 3d model created in Blender or whatever,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 and have a list of optional **textures** with which that model can be rendered.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 So an upper floor bedroom module might have the following renders:
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
058
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 1. Bare masonry - constrained to upland or plateau terrain, and to coastal culture
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 2. Painted masonry - constrained to upland or plateau terrain, and to coastal culture
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 3. Half-timbered - not available on plateau terrain
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 4. Weatherboarded - constrained to forest terrain
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 5. Brick - constrained to arable or arid terrain
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
064
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 of course these are only examples, and also, it's entirely possible to have
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 for example multiple different weatherboard renders for the same module.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 There needs to be a way of rendering what can be built above what: for
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 example, you can't have a masonry clad module over a half timbered one,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 but you can have a half-timbered one over a masonry one.")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
070
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
071 (defrecord BuildingModule
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 [model
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 ^Double length
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
074 ^Double width
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 ^Double height
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 ^Integer load
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 ^clojure.lang.Keyword role
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 ^clojure.lang.IPersistentCollection textures
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 ^clojure.lang.IPersistentCollection exits
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
080 ]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 )
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,458 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/buildings/rectangular.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.buildings.rectangular
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Build buildings with a generally rectangular floow plan.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 ## Motivations
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 Right, the idea behind this namespace is many fold.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 1. To establish the broad principle of genetic buildings, by creating a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 function which reproducibly creates reproducible buildings at specified
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 locations, such that different buildings are credibly varied but a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 building at a specified location is always (modulo economic change) the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 same.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 2. Create good rectangular buildings, and investigate whether a single
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 function can be used to create buildings of more than one family (e.g.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 can it produce flat roofed, north African style, mud brick houses as
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 well as pitch roofed, half timbered northern European houses?)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 3. Establish whether, in my current state of fairly severe mental illness,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 I can actually produce any usable code at all.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
019
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ## Key factors in the creation of a building
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
021
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ### Holding
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
023
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 Every building is on a holding, and, indeed, what I mean by 'building' here
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 may well turn out to be 'the collection of all the permanent structures on
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 a holding. A holding is a polygonal area of the map which does not
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 intersect with any other holding, but for the time being we'll make the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 simplifying assumption that every holding is a rectangular strip, and that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 'urban' holdings are of a reasonably standard width (see Viking-period
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 York) and length. Rural holdings (farms, ?wood lots) may be much larger.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
031
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 ### Terrain
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
033
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 A building is made of the stuff of the place. In a forest, buildings will
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 tend to be wooden; in a terrain with rocky outcrops -- normally found on
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 steep slopes -- stone. On the flat lands where there's river mud, of brick,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 cob, or wattle and daub. So to build a building we need to know the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 terrain. Terrain can be inferred from location but in practice this will
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 be computationally expensive, so we'll pass terrain in as an argument to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 the build function.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
041
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 For the time being we'll pass it in simply as a keyword from a defined set
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 of keywords; later it may be a more sophisticated data structure.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
044
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 ### Culture
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
046
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 People of different cultures build distinctively different buildings, even
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 when using the same materials. So, in our world, a Japanese wooden house
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 looks quite different from an Anglo Saxon stave house which looks quite
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 different from a Canadian log cabin, even though the materials are much the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 same and the tools available to build with are not much different.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
052
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 Culture can affect not just the overall shape of a building but also its
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 finish and surface detail. For example, in many places in England, stone
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 buildings are typically left bare; in rural Scotland, typically painted
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 white or in pastel shades; in Ireland, often quite vivid colours.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
057
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 People may also show religious or cultural symbols on their buildings.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 For all these reasons, we need to know the culture of the occupant when
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 creating a building. Again, this will initially be passed in as a keyword.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
062
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 ### Craft
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
064
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 People in the game world have a craft, and some crafts will require
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 different features in the building. In the broadly late-bronze-age-to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 medieval period within which the game is set, residence and workplace
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 are for most people pretty much the same.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
069
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 So a baker needs an oven, a smith a forge, and so on. All crafts who do
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 some degree retail trade will want a shop front as part of the ground
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 floor of their dwelling. Merchants and bankers will probably have houses
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 that are a bit more showy than others.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
074
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 Whether the 'genetic buildings' idea will ever really produce suitable
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 buildings for aristons I don't know; it seems more likely that significant
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 strongholds (of which there will be relatively few) should all be hand
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 modelled rather than procedurally generated."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 (:require [cc.journeyman.the-great-game.holdings.holding :refer [ProtoHolding]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
080 [cc.journeyman.the-great-game.location.location :refer [ProtoLocation]])
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 (:import [org.apache.commons.math3.random MersenneTwister]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
082
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
083
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
084 (def ^:dynamic *terrain-types*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 "Types of terrain which affect building families. TODO: This is a placeholder;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
086 a more sophisticated model will be needed."
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
087 #{:arable :arid :forest :plateau :upland})
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
088
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
089 (def ^:dynamic *cultures*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 "Cultures which affect building families. TODO: placeholder"
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
091 #{:ariston :coastal :steppe-clans :western-clans :wild-herd})
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
092
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
093 (def ^:dynamic *crafts*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 "Crafts which affect building types in the game. See
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
095 `Populating a game world`. TODO: placeholder"
|
||||
</span><br/>
|
||||
<span class="covered" title="14 out of 14 forms covered">
|
||||
096 #{:baker :banker :butcher :chancellor :innkeeper :lawyer :magus :merchant :miller :priest :scholar :smith :weaver})
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
097
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
098 (def ^:dynamic *building-families*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 "Families of buildings.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
100
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 Each family has
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
103 * terrain types to which it is appropriate;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
104 * crafts to which it is appropriate;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
105 * cultures to which it is appropriate.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
106
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
107 Each generated building will be of one family, and will comprise modules
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
108 taken only from that family."
|
||||
</span><br/>
|
||||
<span class="covered" title="12 out of 12 forms covered">
|
||||
109 {:pitched-rectangular {:terrains #{:arable :forest :upland}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
110 :crafts *crafts*
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
111 :cultures #{:coastal :western-clans}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
112 :modules []}
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
113 :flatroof-rectangular {:terrains #{:arid :plateau}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
114 :crafts *crafts*
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
115 :cultures #{:coastal}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
116 :modules []}})
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
117
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
118
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
119 (defn building-family
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
120 "A building family is essentially a collection of models of building modules
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
121 which can be assembled to create buildings of a particular structural and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
122 architectural style."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
123 [terrain culture craft gene]
|
||||
</span><br/>
|
||||
<span class="partial" title="12 out of 13 forms covered">
|
||||
124 (let [candidates (filter #(and
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
125 ((:terrains %) terrain)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
126 ((:crafts %) craft)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
127 ((:cultures %) culture))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
128 (vals *building-families*))]
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
129 (nth candidates (mod (Math/abs (.nextInt gene)) (count candidates)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
130
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
131 (building-family :arable :coastal :baker (MersenneTwister. 5))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
132
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
133 (defn build!
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
134 "Builds a building, and returns a data structure which represents it. In
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
135 building the building, it adds a model of the building to the representation
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
136 of the world, so it does have a side effect."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
137 [holding terrain culture craft size]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
138 (if (satisfies? ProtoHolding holding)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
139 (let [location (.building-origin holding)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
140 gene (MersenneTwister. (int (+ (* (.easting location) 1000000) (.northing location))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
141 family (building-family terrain culture craft gene)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
142 (if
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
143 (and (instance? ProtoLocation location) (:orientation location))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
144 :stuff
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
145 :nonsense
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
146 ))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
147 :froboz))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
148
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
149 ;; (def ol (cc.journeyman.the-great-game.location.location/OrientedLocation. 123.45 543.76 12.34 0.00 {}))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
150
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,227 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/gossip/gossip.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.gossip.gossip
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Interchange of news events between gossip agents.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 Note that habitual travellers are all gossip agents; specifically, at this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 stage, that means merchants. When merchants are moved we also need to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 update the location of the gossip with the same key.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 Innkeepers are also gossip agents but do not typically move."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 (:require [cc.journeyman.the-great-game.utils :refer [deep-merge]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [cc.journeyman.the-great-game.gossip.news-items :refer [learn-news-item]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
012
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
013
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
014 (defn dialogue
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 "Dialogue between an `enquirer` and an `agent` in this `world`; returns a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 map identical to `enquirer` except that its `:gossip` collection may have
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 additional entries."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;; TODO: not yet written, this is a stub.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 [enquirer respondent world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
020 enquirer)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
021
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
022 (defn gather-news
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 "Gather news for the specified `gossip` in this `world`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 [world gossip]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
025 (let [g (cond (keyword? gossip)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
026 (-> world :gossips gossip)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
027 (map? gossip)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
028 gossip)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
029 (if g
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
030 {:gossips
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
031 {(:id g)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
032 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
033 deep-merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
034 {}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
035 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
036 #(dialogue g % world)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
037 (remove
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
038 #(= g %)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
039 (filter
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
040 #(= (:location %) (:location g))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
041 (vals (:gossips world))))))}}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
042 {})))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
043
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
044 (defn move-gossip
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 "Return a world like this `world` but with this `gossip` moved to this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 `new-location`. Many gossips are essentially shadow-records of agents of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 other types, and the movement of the gossip should be controlled by the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 run function of the type of the record they shadow. The [[#run]] function
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 below does NOT call this function."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 [gossip world new-location]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
051 (let [id (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
052 (map? gossip)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
053 (-> world :gossips gossip :id)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
054 (keyword? gossip)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
055 gossip)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
056 (deep-merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
057 world
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
058 {:gossips
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
059 {id
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
060 {:location new-location}}})))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
061
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
062 (defn run
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 "Return a world like this `world`, with news items exchanged between gossip
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 agents."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 [world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
066 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
067 deep-merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
068 world
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
069 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
070 #(gather-news world %)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
071 (keys (:gossips world)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
072
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
073
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,146 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/holdings/holding.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.holdings.holding
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 (:require [cc.journeyman.the-great-game.agent.agent :refer [ProtoAgent]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 [cc.journeyman.the-great-game.objects.container :refer [ProtoContainer]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [cc.journeyman.the-great-game.objects.game-object :refer [ProtoObject]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 ;; [cc.journeyman.the-great-game.location.location :refer [OrientedLocation]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [cc.journeyman.the-great-game.world.routes :refer []]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ;;; A holding is a polygonal area of the map which does not
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 ;;; intersect with any other holding, or with any road or water feature. For
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 ;;; the time being we'll make the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;;; simplifying assumption that every holding is a rectangular strip, and that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;;; 'urban' holdings are of a reasonably standard width (see Viking-period
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;;; York) and length. Rural holdings (farms, ?wood lots) may be much larger.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
014
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
015 (defprotocol ProtoHolding
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 (frontage
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 [holding]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 "Returns a sequence of two locations representing the edge of the polygon
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 which defines this holding which is considered to be the front.")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 (building-origin
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 [holding]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 "Returns an oriented location - normally the right hand end of the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 frontage, for an urban holding - from which buildings on the holding
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 should be built."))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
025
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
026 (defrecord Holding [perimeter holder]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 ;; Perimeter should be a list of locations in exactly the same sense as a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 ;; route in `cc.journeyman.the-great-game.world.routes`. Some sanity checking
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 ;; is needed to ensure this!
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 ProtoContainer
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 ProtoHolding
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
032 (frontage
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 [holding]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 "TODO: this is WRONG, but will work for now. The frontage should
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 be the side of the perimeter nearest to the nearest existing
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 route."
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
037 [(first (perimeter holding)) (nth (perimeter holding) 1)])
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
038 (building-origin
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 [holding]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 "TODO: again this is WRONG. The default building origin for rectangular
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 buildings should be the right hand end of the frontage when viewed
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 from outside the holding. But that's not general; celtic-style circular
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 buildings should normally be in the centre of their holdings. So probably
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 building-origin becomes a method of building-family rather than of holding."
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
045 (first (frontage holding)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 ProtoObject)
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,143 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/location/location.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.location.location)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
002
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 ;;; There's probably conflict between this sense of a reified location and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 ;;; the simpler sense of a location described in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 ;;; `cc.journeyman.the-great-game.world.location`, q.v. This needs to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 ;;; be resolved!
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
008 (defprotocol ProtoLocation
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 (easting [location]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 "Return the easting of this location")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 (northing [location] "Return the northing of this location")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 (altitude [location]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 "Return the absolute altitude of this location, which may be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 different from the terrain height at this location, if, for
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 example, the location is underground or on an upper floor.")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 (terrain-altitude [location]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 "Return the 'ground level' (altitude of the terrain)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 at this location given this world. TODO: possibly
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 terrain-altitude should be a method of the world.")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 (settlement [location]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 "Return the settlement record of the settlement in this world
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 within whose parish polygon this location exists, or if none
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 whose centre (inn location) is closest to this location"))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
024
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
025
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
026 (defrecord Location [^Double easting ^Double northing ^Double altitude world]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 ProtoLocation
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
028 (easting [l] (:easting l))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
029 (northing [l] (:northing l))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
030 (altitude [l] (:altitude l))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
031 (terrain-altitude [l] 0.0) ;; TODO
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
032 (settlement [l] :tchahua))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
033
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
034 (defrecord OrientedLocation
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 ;; "Identical to a Location except having, additionally, an orientation"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 [^Double easting ^Double northing ^Double altitude ^Double orientation world]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 ProtoLocation
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
038 (easting [l] (:easting l))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
039 (northing [l] (:northing l))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
040 (altitude [l] (:altitude l))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
041 (terrain-altitude [l] 0.0) ;; TODO
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
042 (settlement [l] :tchahua)) ;; TODO
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
043
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 ;; (.settlement (OrientedLocation. 123.45 543.76 12.34 0.00 {}))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
045
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,260 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/merchants/markets.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.merchants.markets
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Adjusting quantities and prices in markets."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [taoensso.timbre :as l :refer [info error]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [cc.journeyman.the-great-game.utils :refer [deep-merge]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
005
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
006 (defn new-price
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 "If `stock` is greater than the maximum of `supply` and `demand`, then
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 there is surplus and `old` price is too high, so shold be reduced. If
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 lower, then it is too low and should be increased."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [old stock supply demand]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
011 (let
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
012 [delta (dec' (/ (max supply demand 1) (max stock 1)))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
013 scaled (/ delta 100)]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
014 (+ old scaled)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
015
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
016
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
017 (defn adjust-quantity-and-price
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 "Adjust the quantity of this `commodity` currently in stock in this `city`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 of this `world`. Return a fragmentary world which can be deep-merged into
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 this world."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 [world city commodity]
|
||||
</span><br/>
|
||||
<span class="partial" title="2 out of 4 forms covered">
|
||||
022 (let [c (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
023 (keyword? city) (-> world :cities city)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
024 (map? city) city)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
025 id (:id c)
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
026 p (or (-> c :prices commodity) 0)
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
027 d (or (-> c :demands commodity) 0)
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
028 st (or (-> c :stock commodity) 0)
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
029 su (or (-> c :supplies commodity) 0)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
030 decrement (min st d)
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 6 forms covered">
|
||||
031 increment (cond
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 ;; if we've two turns' production of this commodity in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 ;; stock, halt production
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
034 (> st (* su 2))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 ;; if it is profitable to produce this commodity, the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 ;; craftspeople of the city will do so.
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
038 (> p 1) su
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 ;; otherwise, if there isn't a turn's production in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 ;; stock, top up the stock, so that there's something for
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 ;; incoming merchants to buy
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
042 (> su st)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
043 (- su st)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 :else
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 0)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
046 n (new-price p st su d)]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
047 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
048 (not= p n)
|
||||
</span><br/>
|
||||
<span class="covered" title="25 out of 25 forms covered">
|
||||
049 (l/info "Price of" commodity "at" id "has changed from" (float p) "to" (float n)))
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
050 {:cities {id
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
051 {:stock
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
052 {commodity (+ (- st decrement) increment)}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 :prices
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
054 {commodity n}}}}))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
055
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
056
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
057 (defn update-markets
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 "Return a world like this `world`, with quantities and prices in markets
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 updated to reflect supply and demand. If `city` or `city` and `commodity`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 are specified, return a fragmentary world with only the changes for that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 `city` (and `commodity` if specified) populated."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 ([world]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
063 (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
064 deep-merge
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
065 world
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
066 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
067 #(update-markets world %)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
068 (keys (:cities world)))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 ([world city]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
070 (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
071 deep-merge
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
072 {}
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
073 (map #(update-markets world city %)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
074 (keys (:commodities world)))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 ([world city commodity]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
076 (adjust-quantity-and-price world city commodity)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
077
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
078
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
079 (defn run
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
080 "Return a world like this `world`, with quantities and prices in markets
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 updated to reflect supply and demand."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 [world]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
083 (update-markets world))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
084
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,326 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/merchants/merchant_utils.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.merchants.merchant-utils
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Useful functions for doing low-level things with merchants.")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
004 (defn expected-price
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 "Find the price anticipated, given this `world`, by this `merchant` for
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 this `commodity` in this `city`. If no information, assume 1.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 `merchant` should be passed as a map, `commodity` and `city` should be passed as keywords."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [merchant commodity city]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
009 (or
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
010 (:price
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
011 (last
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
012 (sort-by
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 :date
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
014 (-> merchant :known-prices city commodity))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 1))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
016
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
017 (defn burden
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 "The total weight of the current cargo carried by this `merchant` in this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 `world`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 [merchant world]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
021 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
022 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
023 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
024 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
025 merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
026 cargo (or (:stock m) {})]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
027 (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
028 +
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 0
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
030 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
031 #(* (cargo %) (-> world :commodities % :weight))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
032 (keys cargo)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
033
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
034
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
035 (defn can-carry
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 "Return the number of units of this `commodity` which this `merchant`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 can carry in this `world`, given their current burden."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 [merchant world commodity]
|
||||
</span><br/>
|
||||
<span class="partial" title="3 out of 4 forms covered">
|
||||
039 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
040 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
041 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
042 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
043 merchant)]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
044 (max
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 0
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
046 (quot
|
||||
</span><br/>
|
||||
<span class="partial" title="12 out of 13 forms covered">
|
||||
047 (- (or (:capacity m) 0) (burden m world))
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
048 (-> world :commodities commodity :weight)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
049
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
050 (defn can-afford
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 "Return the number of units of this `commodity` which this `merchant`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 can afford to buy in this `world`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 [merchant world commodity]
|
||||
</span><br/>
|
||||
<span class="partial" title="3 out of 4 forms covered">
|
||||
054 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
055 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
056 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
057 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
058 merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
059 l (:location m)]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
060 (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
061 (nil? m)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
062 (throw (Exception. "No merchant?"))
|
||||
</span><br/>
|
||||
<span class="covered" title="14 out of 14 forms covered">
|
||||
063 (or (nil? l) (nil? (-> world :cities l)))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
064 (throw (Exception. (str "No known location for merchant " m)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 :else
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
066 (quot
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
067 (:cash m)
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
068 (-> world :cities l :prices commodity)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
069
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
070 (defn add-stock
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 "Where `a` and `b` are both maps all of whose values are numbers, return
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 a map whose keys are a union of the keys of `a` and `b` and whose values
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 are the sums of their respective values."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
074 [a b]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
075 (reduce
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
076 merge
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
077 a
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
078 (map
|
||||
</span><br/>
|
||||
<span class="partial" title="19 out of 20 forms covered">
|
||||
079 #(hash-map % (+ (or (a %) 0) (or (b %) 0)))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
080 (keys b))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
081
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
082 (defn add-known-prices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
083 "Add the current prices at this `merchant`'s location in the `world`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
084 to a new cache of known prices, and return it."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 [merchant world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
086 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
087 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
088 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
089 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
090 merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
091 k (or (:known-prices m) {})
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
092 l (:location m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
093 d (or (:date world) 0)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
094 p (-> world :cities l :prices)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
095 (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
096 (nil? m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
097 (throw (Exception. "No merchant?"))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 14 forms covered">
|
||||
098 (or (nil? l) (nil? (-> world :cities l)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
099 (throw (Exception. (str "No known location for merchant " m)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
100 :else
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
101 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
102 merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
103 k
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
104 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
105 #(hash-map % (apply vector cons {:price (p %) :date d} (k %)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
106 (-> world :commodities keys))))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,92 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/merchants/merchants.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.merchants.merchants
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Trade planning for merchants, primarily."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [cc.journeyman.the-great-game.utils :refer [deep-merge]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [cc.journeyman.the-great-game.merchants.strategies.simple :refer [move-merchant]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [taoensso.timbre :as l]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
006
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
008 (defn run
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 "Return a partial world based on this `world`, but with each merchant moved."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
011 (try
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
012 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
013 deep-merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
014 world
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
015 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
016 #(try
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
017 (let [move-fn (or
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
018 (-> world :merchants % :move-fn)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 move-merchant)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
020 (apply move-fn (list % world)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 (catch Exception any
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 18 forms covered">
|
||||
022 (l/error any "Failure while moving merchant " %)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
023 {}))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
024 (keys (:merchants world))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 (catch Exception any
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
026 (l/error any "Failure while moving merchants")
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
027 world)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
028
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,485 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/merchants/planning.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.merchants.planning
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Trade planning for merchants, primarily. This follows a simple-minded
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 generate-and-test strategy and currently generates plans for all possible
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 routes from the current location. This may not scale. Also, routes do not
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 currently have cost or risk associated with them."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 (:require [cc.journeyman.the-great-game.utils :refer [deep-merge make-target-filter]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [cc.journeyman.the-great-game.merchants.merchant-utils :refer [can-afford can-carry expected-price]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [cc.journeyman.the-great-game.world.routes :refer [find-route]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [cc.journeyman.the-great-game.world.world :refer [actual-price default-world]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
011 (defn generate-trade-plans
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 "Generate all possible trade plans for this `merchant` and this `commodity`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 in this `world`.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
014
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 Returned plans are maps with keys:
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
016
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 * :merchant - the id of the `merchant` for whom the plan was created;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 * :origin - the city from which the trade starts;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 * :destination - the city to which the trade is planned;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 * :commodity - the `commodity` to be carried;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 * :buy-price - the price at which that `commodity` can be bought;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 * :expected-price - the price at which the `merchant` anticipates
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 that `commodity` can be sold;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 * :distance - the number of stages in the planned journey
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 * :dist-to-home - the distance from `destination` to the `merchant`'s
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 home city."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 [merchant world commodity]
|
||||
</span><br/>
|
||||
<span class="partial" title="3 out of 4 forms covered">
|
||||
028 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
029 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
030 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
031 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
032 merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
033 origin (:location m)]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
034 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
035 #(hash-map
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
036 :merchant (:id m)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
037 :origin origin
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 :destination %
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
039 :commodity commodity
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
040 :buy-price (actual-price world commodity origin)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
041 :expected-price (expected-price
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
042 m
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
043 commodity
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 %)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
045 :distance (count
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
046 (find-route world origin %))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
047 :dist-to-home (count
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
048 (find-route
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
049 world
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
050 (:home m)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 %)))
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
052 (remove #(= % origin) (-> world :cities keys)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
053
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
054 (defn nearest-with-targets
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 "Return the distance to the nearest destination among those of these
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 `plans` which match these `targets`. Plans are expected to be plans
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 as returned by `generate-trade-plans`, q.v.; `targets` are expected to be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 as accepted by `make-target-filter`, q.v."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 [plans targets]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
060 (apply
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
061 min
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
062 (map
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 :distance
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
064 (filter
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
065 (make-target-filter targets)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
066 plans))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
067
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
068 (defn plan-trade
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 "Find the best destination in this `world` for this `commodity` given this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 `merchant` and this `origin`. If two cities are anticipated to offer the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 same price, the nearer should be preferred; if two are equally distant, the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 ones nearer to the merchant's home should be preferred.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 `merchant` may be passed as a map or a keyword; `commodity` should be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
074 passed as a keyword.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
075
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 The returned plan is a map with keys:
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
077
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
078 * :merchant - the id of the `merchant` for whom the plan was created;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 * :origin - the city from which the trade starts;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
080 * :destination - the city to which the trade is planned;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 * :commodity - the `commodity` to be carried;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 * :buy-price - the price at which that `commodity` can be bought;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
083 * :expected-price - the price at which the `merchant` anticipates
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
084 that `commodity` can be sold;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 * :distance - the number of stages in the planned journey
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
086 * :dist-to-home - the distance from `destination` to the `merchant`'s
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 home city."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
088 [merchant world commodity]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
089 (let [plans (generate-trade-plans merchant world commodity)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
090 best-prices (filter
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
091 (make-target-filter
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
092 [[:expected-price
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
093 (apply
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
094 max
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
095 (filter number? (map :expected-price plans)))]])
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
096 plans)]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
097 (first
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
098 (sort-by
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 ;; all other things being equal, a merchant would prefer to end closer
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
100 ;; to home.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
101 #(- 0 (:dist-to-home %))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102 ;; a merchant will seek the best price, but won't go further than
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
103 ;; needed to get it.
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
104 (filter
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
105 (make-target-filter
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
106 [[:distance
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
107 (apply min (filter number? (map :distance best-prices)))]])
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
108 best-prices)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
109
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
110 (defn augment-plan
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
111 "Augment this `plan` constructed in this `world` for this `merchant` with
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
112 the `:quantity` of goods which should be bought and the `:expected-profit`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
113 of the trade.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
114
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
115 Returns the augmented plan."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
116 [merchant world plan]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
117 (let [c (:commodity plan)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
118 o (:origin plan)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
119 q (min
|
||||
</span><br/>
|
||||
<span class="partial" title="4 out of 5 forms covered">
|
||||
120 (or
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
121 (-> world :cities o :stock c)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
122 0)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
123 (can-carry merchant world c)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
124 (can-afford merchant world c))
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
125 p (* q (- (:expected-price plan) (:buy-price plan)))]
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
126 (assoc plan :quantity q :expected-profit p)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
127
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
128 (defn select-cargo
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
129 "A `merchant`, in a given location in a `world`, will choose to buy a cargo
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
130 within the limit they are capable of carrying, which they can anticipate
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
131 selling for a profit at a destination."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
132 [merchant world]
|
||||
</span><br/>
|
||||
<span class="partial" title="2 out of 4 forms covered">
|
||||
133 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
134 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
135 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
136 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
137 merchant)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
138 origin (:location m)
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
139 available (-> world :cities origin :stock)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
140 plans (map
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
141 #(augment-plan
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
142 m
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
143 world
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
144 (plan-trade m world %))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
145 (filter
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
146 #(let [q (-> world :cities origin :stock %)]
|
||||
</span><br/>
|
||||
<span class="partial" title="8 out of 9 forms covered">
|
||||
147 (and (number? q) (pos? q)))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
148 (keys available)))]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
149 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
150 (not (empty? plans))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
151 (first
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
152 (sort-by
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
153 #(- 0 (:dist-to-home %))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
154 (filter
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
155 (make-target-filter
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
156 [[:expected-profit
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
157 (apply max (filter number? (map :expected-profit plans)))]])
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
158 plans))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
159
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,527 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../../coverage.css"/> <title> cc/journeyman/the_great_game/merchants/strategies/simple.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.merchants.strategies.simple
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Default trading strategy for merchants.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 The simple strategy buys a single product in the local market if there is
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 one which can be traded profitably, trades it to the chosen target market,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 and sells it there. If there is no commodity locally which can be traded
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 profitably, moves towards home with no cargo. If at home and no commodity
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 can be traded profitably, does not move."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 (:require [taoensso.timbre :as l :refer [info error spy]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [cc.journeyman.the-great-game.utils :refer [deep-merge]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 [cc.journeyman.the-great-game.gossip.gossip :refer [move-gossip]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 [cc.journeyman.the-great-game.merchants.planning :refer [augment-plan plan-trade select-cargo]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 [cc.journeyman.the-great-game.merchants.merchant-utils :refer
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 [add-stock add-known-prices]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 [cc.journeyman.the-great-game.world.routes :refer [find-route]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
016
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
017 (defn plan-and-buy
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 "Return a world like this `world`, in which this `merchant` has planned
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 a new trade, and bought appropriate stock for it. If no profitable trade
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 can be planned, the merchant is simply moved towards their home."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 [merchant world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
022 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
023 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
024 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
025 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
026 merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
027 id (:id m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
028 location (:location m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
029 market (-> world :cities location)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
030 plan (select-cargo merchant world)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
031 (l/debug "plan-and-buy: merchant" id)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
032 (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
033 (seq? plan)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
034 (let
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
035 [c (:commodity plan)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
036 p (* (:quantity plan) (:buy-price plan))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
037 q (:quantity plan)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 26 forms covered">
|
||||
038 (l/info "Merchant" id "bought" q "units of" c "at" location "for" p plan)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
039 {:merchants
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
040 {id
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
041 {:stock (add-stock (:stock m) {c q})
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
042 :cash (- (:cash m) p)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
043 :known-prices (add-known-prices m world)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
044 :plan plan}}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 :cities
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
046 {location
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 16 forms covered">
|
||||
047 {:stock (assoc (:stock market) c (- (-> market :stock c) q))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
048 :cash (+ (:cash market) p)}}})
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 ;; if no plan, then if at home stay put
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
050 (= (:location m) (:home m))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
051 (do
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 19 forms covered">
|
||||
052 (l/info "Merchant" id "remains at home in" location)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
053 {})
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 ;; else move towards home
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 :else
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
056 (let [route (find-route world location (:home m))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
057 next-location (nth route 1)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 21 forms covered">
|
||||
058 (l/info "No trade possible at" location "; merchant" id "moves to" next-location)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
059 (merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
060 {:merchants
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
061 {id
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
062 {:location next-location}}}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
063 (move-gossip id world next-location))))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
064
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
065 (defn re-plan
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 "Having failed to sell a cargo at current location, re-plan a route to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 sell the current cargo. Returns a revised world."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 [merchant world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
069 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
070 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
071 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
072 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
073 merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
074 id (:id m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
075 location (:location m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
076 plan (augment-plan m world (plan-trade m world (-> m :plan :commodity)))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
077 (l/debug "re-plan: merchant" id)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
078 (deep-merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
079 world
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
080 {:merchants
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
081 {id
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
082 {:plan plan}}})))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
083
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
084 (defn sell-and-buy
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 "Return a new world like this `world`, in which this `merchant` has sold
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
086 their current stock in their current location, and planned a new trade, and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 bought appropriate stock for it."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
088 ;; TODO: this either sells the entire cargo, or, if the market can't afford
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 ;; it, none of it. And it does not cope with selling different commodities
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 ;; in different markets.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 [merchant world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
092 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
093 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
094 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
095 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
096 merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
097 id (:id m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
098 location (:location m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
099 market (-> world :cities location)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
100 stock-value (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
101 +
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
102 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
103 #(* (-> m :stock %) (-> market :prices m))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
104 (keys (:stock m))))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
105 (l/debug "sell-and-buy: merchant" id)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
106 (if
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
107 (>= (:cash market) stock-value)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
108 (do
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 25 forms covered">
|
||||
109 (l/info "Merchant" id "sells" (:stock m) "at" location "for" stock-value)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
110 (plan-and-buy
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
111 merchant
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
112 (deep-merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
113 world
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
114 {:merchants
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
115 {id
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
116 {:stock {}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
117 :cash (+ (:cash m) stock-value)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
118 :known-prices (add-known-prices m world)}}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
119 :cities
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
120 {location
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
121 {:stock (add-stock (:stock m) (:stock market))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
122 :cash (- (:cash market) stock-value)}}})))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
123 ;; else
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
124 (re-plan merchant world))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
125
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
126 (defn move-merchant
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
127 "Handle general en route movement of this `merchant` in this `world`;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
128 return a (partial or full) world like this `world` but in which the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
129 merchant may have been moved ot updated."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
130 [merchant world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
131 (let [m (cond
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
132 (keyword? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
133 (-> world :merchants merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
134 (map? merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
135 merchant)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
136 id (:id m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 16 forms covered">
|
||||
137 at-destination? (and (:plan m) (= (:location m) (-> m :plan :destination)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
138 plan (:plan m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
139 next-location (if plan
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
140 (nth
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
141 (find-route
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
142 world
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
143 (:location m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
144 (:destination plan))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
145 1)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
146 (:location m))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 24 forms covered">
|
||||
147 (l/debug "move-merchant: merchant" id "at" (:location m)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
148 "destination" (-> m :plan :destination) "next" next-location
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
149 "at destination" at-destination?)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
150 (cond
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
151 ;; if the merchant is at the destination of their current plan
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
152 ;; sell all cargo and repurchase.
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
153 at-destination?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
154 (sell-and-buy merchant world)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
155 ;; if they don't have a plan, seek to create one
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
156 (nil? plan)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
157 (plan-and-buy merchant world)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
158 ;; otherwise, move one step towards their destination
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
159 (and next-location (not= next-location (:location m)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
160 (do
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 23 forms covered">
|
||||
161 (l/info "Merchant " id " moving from " (:location m) " to " next-location)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
162 (deep-merge
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
163 {:merchants
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
164 {id
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
165 {:location next-location
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
166 :known-prices (add-known-prices m world)}}}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
167 (move-gossip id world next-location)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
168 :else
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
169 (do
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 19 forms covered">
|
||||
170 (l/info "Merchant" id "has plan but no next-location; currently at"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
171 (:location m) ", destination is" (:destination plan))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
172 world))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
173
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,41 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/objects/container.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.objects.container
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 (:require
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 [cc.journeyman.the-great-game.objects.game-object :refer :all]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
004
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
005 (defprotocol ProtoContainer
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 (contents
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [container]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 "Return a sequence of the contents of this `container`, or `nil` if empty.")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 (is-empty?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 [container]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 "Return `true` if this `container` is empty, else `false`."))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,71 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/objects/game_object.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.objects.game-object
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Anything at all in the game world")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
004 (defprotocol ProtoObject
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 "An object in the world"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 (id [object] "Returns the unique id of this object.")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 (reify-object
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [object]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 "Adds this `object` to the global object list. If the `object` has a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 non-nil value for its `id` method, keys it to that id - **but** if the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 id value is already in use, throws a hard exception. Returns the id to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 which the object is keyed in the global object list."))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
013
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
014 (defrecord GameObject
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 [id]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;; "An object in the world"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ProtoObject
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
018 (id [_] id)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
019 (reify-object [object]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 "TODO: doesn't work yet"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
021 object))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
224
docs/cloverage/cc/journeyman/the_great_game/playroom.clj.html
Normal file
224
docs/cloverage/cc/journeyman/the_great_game/playroom.clj.html
Normal file
|
@ -0,0 +1,224 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../coverage.css"/> <title> cc/journeyman/the_great_game/playroom.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.playroom
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 (:require [jme-clj.core :refer [add add-to-root box defsimpleapp fly-cam geo
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 get* get-state load-texture rotate run set*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 setc set-state start unshaded-mat]])
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 (:import [com.jme3.math ColorRGBA]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
006
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ;; At present this file is just somewhere to play around with jme-clj examples
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
008
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
009 (declare app)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
011 (defn init []
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
012 (let [cube (geo "jMonkey cube" (box 1 1 1))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
013 mat (unshaded-mat)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
014 (set* mat :texture "ColorMap" (load-texture "textures/Monkey.jpg"))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
015 (set* cube :material mat)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
016 (add-to-root cube)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
017 {:cube cube}))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
018
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;; Let's create simple-update fn with no body for now.
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
020 (defn simple-update [tpf]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
021 (let [{:keys [cube]} (get-state)]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
022 (rotate cube 0 (* 2 tpf) 0)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
023
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
024
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;; Kills the running app var and closes its window.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;; (unbind-app #'app)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
027
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 ;; We define the `app` var.
|
||||
</span><br/>
|
||||
<span class="partial" title="115 out of 215 forms covered">
|
||||
029 (defsimpleapp app
|
||||
</span><br/>
|
||||
<span class="partial" title="36 out of 42 forms covered">
|
||||
030 :opts {:show-settings? false
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 :pause-on-lost-focus? false
|
||||
</span><br/>
|
||||
<span class="partial" title="66 out of 77 forms covered">
|
||||
032 :settings {:title "My JME Game"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 :load-defaults? true
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 :frame-rate 60
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 :width 800
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 :height 600}}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
037 :init init
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
038 :update simple-update)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
039
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 ;; (start app)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
041
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 ;; Reinitialises the running app
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 ;;(run app
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 ;; (re-init init))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 ;; By default, there is a Fly Camera attached to the app that you can control with W, A, S and D keys.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 ;; Let's increase its movement speed. Now, you fly faster :)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 ;; (run app
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 ;; (set* (fly-cam) :move-speed 15))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
050
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
051
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 ;; Updates the app
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 ;; (run app
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 ;; (let [{:keys [cube]} (get-state)]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 ;; (set* cube :local-translation (add (get* cube :local-translation) 1 1 1))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
056
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 ;; Updates the app adding a second cube
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 ;; (run app
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
059 ;; (let [cube (geo "jMonkey cube" (box 1 1 1))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 ;; mat (unshaded-mat)]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 ;; (set* mat :texture "ColorMap" (load-texture "textures/Monkey.jpg"))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 ;; (setc cube
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 ;; :material mat
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 ;; :local-translation [-3 0 0])
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 ;; (add-to-root cube)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 ;; (set-state :cube2 cube)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 ;; We added the new cube, but it's not rotating. We need to update the simple-update fn.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 ;; (defn simple-update [tpf]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 ;; (let [{:keys [cube cube2]} (get-state)]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 ;; (rotate cube 0 (* 2 tpf) 0)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 ;; (rotate cube2 0 (* 2 tpf) 0)))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
440
docs/cloverage/cc/journeyman/the_great_game/time.clj.html
Normal file
440
docs/cloverage/cc/journeyman/the_great_game/time.clj.html
Normal 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> cc/journeyman/the_great_game/time.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.time
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 (:require [clojure.string :as s]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
004 (def game-start-time
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 "The start time of this run."
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
006 (System/currentTimeMillis))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
008 (def ^:const game-day-length
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 "The Java clock advances in milliseconds, which is fine.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 But we need game-days to be shorter than real world days.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 A Witcher 3 game day is 1 hour 36 minutes, or 96 minutes, which is
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 presumably researched. Round it up to 100 minutes for easier
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 calculation."
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
014 (* 100 ;; minutes per game day
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 60 ;; seconds per minute
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 1000)) ;; milliseconds per second
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
017
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
018 (defn now
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 "For now, we'll use Java timestamp for time; ultimately, we need a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 concept of game-time which allows us to drive day/night cycle, seasons,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 et cetera, but what matters about time is that it is a value which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 increases."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 []
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
024 (System/currentTimeMillis))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
025
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
026 (def ^:const canonical-ordering-of-houses
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 "The canonical ordering of religious houses."
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
028 [:eye
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 :foot
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 :nose
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 :hand
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 :ear
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 :mouth
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 :stomach
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 :furrow
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 :plough])
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
037
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
038 (def ^:const days-of-week
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 "The eight-day week of the game world. This differs from the canonical
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 ordering of houses in that it omits the eye."
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
041 (rest canonical-ordering-of-houses))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
042
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
043 (def ^:const days-in-week
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 "This world has an eight day week."
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
045 (count days-of-week))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
046
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
047 (def ^:const seasons-of-year
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 "The ordering of seasons in the year is different from the canonical
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 ordering of the houses, for reasons of the agricultural cycle."
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
050 [:foot
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 :nose
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 :hand
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 :ear
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 :mouth
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 :stomach
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 :plough
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 :furrow
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 :eye])
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
059
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
060 (def ^:const seasons-in-year
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 "Nine seasons in a year, one for each house (although the order is
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 different."
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
063 (count seasons-of-year))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
064
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
065 (def ^:const weeks-of-season
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 "To fit nine seasons of eight day weeks into 365 days, each must be of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 five weeks."
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
068 [:first :second :third :fourth :fifth])
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
069
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
070 (def ^:const weeks-in-season
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 "To fit nine seasons of eight day weeks into 365 days, each must be of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 five weeks."
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
073 (count weeks-of-season))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
074
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
075 (def ^:const days-in-season
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
076 (* weeks-in-season days-in-week))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
077
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
078 (defn game-time
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 "With no arguments, the current game time. If a Java `timestamp` value is
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
080 passed (as a `long`), the game time represented by that value."
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
081 ([] (game-time (now)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 ([timestamp]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
083 (- timestamp game-start-time)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
084
|
||||
</span><br/>
|
||||
<span class="covered" title="54 out of 54 forms covered">
|
||||
085 (defmacro day-of-year
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
086 "The day of the year represented by this `game-time`, ignoring leap years."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 [game-time]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
088 `(mod (long (/ ~game-time game-day-length)) 365))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
089
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
090 (def waiting-day?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 "Does this `game-time` represent a waiting day?"
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
092 (memoize
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
093 ;; we're likely to call this several times in quick succession on the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 ;; same timestamp
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
095 (fn [game-time]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
096 (>=
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
097 (day-of-year game-time)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
098 (* seasons-in-year weeks-in-season days-in-week)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
099
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
100 (defn day
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 "Day of the eight-day week represented by this `game-time`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102 [game-time]
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
103 (let [day-of-week (mod (day-of-year game-time) days-in-week)]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
104 (if (waiting-day? game-time)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
105 (nth weeks-of-season day-of-week)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
106 (nth days-of-week day-of-week))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
107
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
108 (defn week
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 "Week of season represented by this `game-time`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 [game-time]
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
111 (let [day-of-season (mod (day-of-year game-time) days-in-season)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
112 week (/ day-of-season days-in-week)]
|
||||
</span><br/>
|
||||
<span class="partial" title="4 out of 5 forms covered">
|
||||
113 (if (waiting-day? game-time)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
114 :waiting
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
115 (nth weeks-of-season week))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
116
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
117 (defn season
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
118 [game-time]
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
119 (let [season (int (/ (day-of-year game-time) days-in-season))]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
120 (if (waiting-day? game-time)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
121 :waiting
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
122 (nth seasons-of-year season))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
123
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
124 (defn date-string
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
125 "Return a correctly formatted date for this `game-time` in the calendar of
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
126 the Great Place."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
127 [game-time]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
128 (s/join
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
129 " "
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
130 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
131 (waiting-day? game-time)
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
132 [(s/capitalize
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
133 (name
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
134 (nth
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
135 weeks-of-season
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
136 (mod (day-of-year game-time) days-in-week))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
137 "waiting day"]
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
138 [(s/capitalize (name (week game-time)))
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
139 (s/capitalize (name (day game-time)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
140 "of the"
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
141 (s/capitalize (name (season game-time)))])))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
142
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
143
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
144
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
191
docs/cloverage/cc/journeyman/the_great_game/utils.clj.html
Normal file
191
docs/cloverage/cc/journeyman/the_great_game/utils.clj.html
Normal file
|
@ -0,0 +1,191 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../coverage.css"/> <title> cc/journeyman/the_great_game/utils.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.utils)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
002
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
003 (defn cyclic?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 "True if two or more elements of `route` are identical"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [route]
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
006 (not= (count route)(count (set route))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
007
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
008 (defn deep-merge
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 "Recursively merges maps. Stolen from
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 https://dnaeon.github.io/recursively-merging-maps-in-clojure/"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 [& maps]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
012 (letfn [(m [& xs]
|
||||
</span><br/>
|
||||
<span class="covered" title="17 out of 17 forms covered">
|
||||
013 (if (some #(and (map? %) (not (record? %))) xs)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
014 (apply merge-with m xs)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
015 (last xs)))]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
016 (reduce m maps)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
017
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
018 (defn make-target-filter
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 "Construct a filter which, when applied to a list of maps,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 will pass those which match these `targets`, where each target
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 is a tuple [key value]."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;; TODO: this would probably be more elegant as a macro
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 [targets]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
024 (eval
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
025 (list
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 'fn
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
027 (vector 'm)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
028 (cons
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 'and
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
030 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
031 #(list
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 '=
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
033 (list (first %) 'm)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
034 (nth % 1))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
035 targets)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
036
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
037 (defn value-or-default
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 "Return the value of this key `k` in this map `m`, or this `dflt` value if
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 there is none."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 [m k dflt]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
041 (or (when (map? m) (m k)) dflt))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
042
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 ;; (value-or-default {:x 0 :y 0 :altitude 7} :altitude 8)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 ;; (value-or-default {:x 0 :y 0 :altitude 7} :alt 8)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 ;; (value-or-default nil :altitude 8)
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
046
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
047 (defn truthy?
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 "Returns `true` unless `val` is `nil`, `false` or an empty sequence.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 Otherwise always 'false'; never any other value."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 [val]
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
051 (and (or val false) true))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
052
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
053
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
054 (defn inc-or-one
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 "If this `val` is a number, return that number incremented by one; otherwise,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 return 1. TODO: should probably be in `utils`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 [val]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
058 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
059 (number? val)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
060 (inc val)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 1))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,485 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/world/heightmap.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.world.heightmap
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Functions dealing with the tessellated multi-layer heightmap."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [clojure.math.numeric-tower :refer [expt sqrt]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [mw-engine.core :refer []]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [mw-engine.heightmap :refer [apply-heightmap]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [mw-engine.utils :refer [get-cell in-bounds? map-world]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [cc.journeyman.the-great-game.utils :refer [value-or-default]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
008
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 ;; It's not at all clear to me yet what the workflow for getting a MicroWorld
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 ;; map into The Great Game, and whether it passes through Walkmap to get here.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;; This file as currently written assumes it doesn't.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
012
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;; It's utterly impossible to hold a whole continent at one metre scale in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;; memory at one time. So we have to be able to regenerate high resolution
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;; surfaces from much lower resolution heightmaps.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;; Thus to reproduce a segment of surface at a particular level of detail,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;; we:
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;; 1. load the base heightmap into a grid (see
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;; `mw-engine.heightmap/apply-heightmap`);
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;; 2. scale the base hightmap to kilometre scale (see `scale-grid`);
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 ;; 3. exerpt the portion of that that we want to reproduce (see `exerpt-grid`);
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;; 4. interpolate that grid to get the resolution we require (see
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 ;; `interpolate-grid`);
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;; 5. create an appropriate purturbation grid from the noise map(s) for the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 ;; same coordinates to break up the smooth interpolation;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 ;; 6. sum the altitudes of the two grids.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 ;; In production this will have to be done **very** fast!
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
030
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
031 (def ^:dynamic *base-map* "resources/maps/heightmap.png")
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
032 (def ^:dynamic *noise-map* "resources/maps/noise.png")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
033
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
034 (defn scale-grid
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 "multiply all `:x` and `:y` values in this `grid` by this `n`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 [grid n]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 19 forms covered">
|
||||
037 (map-world grid (fn [w c x] (assoc c :x (* (:x c) n) :y (* (:y c) n)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
038
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
039
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
040
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 ;; Each of the east-west curve and the north-south curve are of course two
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 ;; dimensional curves; the east-west curve is in the :x/:z plane and the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 ;; north-south curve is in the :y/:z plane (except, perhaps unwisely,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 ;; we've been using :altitude to label the :z plane). We have a library
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 ;; function `walkmap.edge/intersection2d`, but as currently written it
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 ;; can only find intersections in :x/:y plane.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 ;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 ;; TODO: rewrite the function so that it can use arbitrary coordinates.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 ;; AFTER TRYING: OK, there are too many assumptions about the way that
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 ;; function is written to allow for easy rotation. TODO: think!
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
051
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
052 (defn interpolate-altitude
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 "Return the altitude of the point at `x-offset`, `y-offset` within this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 `cell` having this `src-width`, taken from this `grid`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 [cell grid src-width x-offset y-offset ]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
056 (let [c-alt (:altitude cell)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
057 n-alt (or (:altitude (get-cell grid (:x cell) (dec (:y cell)))) c-alt)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
058 w-alt (or (:altitude (get-cell grid (inc (:x cell)) (:y cell))) c-alt)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
059 s-alt (or (:altitude (get-cell grid (:x cell) (inc (:y cell)))) c-alt)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 17 forms covered">
|
||||
060 e-alt (or (:altitude (get-cell grid (dec (:x cell)) (:y cell))) c-alt)]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 ;; TODO: construct two curves (arcs of circles good enough for now)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 ;; n-alt...c-alt...s-alt and e-alt...c-alt...w-alt;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 ;; then interpolate x-offset along e-alt...c-alt...w-alt and y-offset
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
064 ;; along n-alt...c-alt...s-alt;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 ;; then return the average of the two
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
066
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 0))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
068
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
069 (defn interpolate-cell
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 "Construct a grid (array of arrays) of cells each of width `target-width`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
071 from this `cell`, of width `src-width`, taken from this `grid`"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 [cell grid src-width target-width]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
073 (let [offsets (map #(* target-width %) (range (/ src-width target-width)))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
074 (into
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
075 []
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
076 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
077 (fn [r]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
078 (into
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
079 []
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
080 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
081 (fn [c]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
082 (assoc cell
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
083 :x (+ (:x cell) c)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
084 :y (+ (:y cell) r)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
085 :altitude (interpolate-altitude cell grid src-width c r)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
086 offsets)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
087 offsets))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
088
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
089 (defn interpolate-grid
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
090 "Return a grid interpolated from this `grid` of rows, cols given scaling
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 from this `src-width` to this `target-width`"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
092 [grid src-width target-width]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
093 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
094 concat
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
095 (into
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
096 []
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
097 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
098 (fn [row]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
099 (reduce
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
100 (fn [g1 g2]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 15 forms covered">
|
||||
101 (into [] (map #(into [] (concat %1 %2)) g1 g2)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 13 forms covered">
|
||||
102 (into [] (map #(interpolate-cell % grid src-width target-width) row))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
103 grid))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
104
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
105 (defn excerpt-grid
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
106 "Return that section of this `grid` where the `:x` co-ordinate of each cell
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
107 is greater than or equal to this `x-offset`, the `:y` co-ordinate is greater
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
108 than or equal to this `y-offset`, whose width is not greater than this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 `width`, and whose height is not greater than this `height`."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 [grid x-offset y-offset width height]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
111 (into
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
112 []
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
113 (remove
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
114 nil?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
115 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
116 (fn [row]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
117 (when
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
118 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 7 forms covered">
|
||||
119 (>= (:y (first row)) y-offset)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
120 (< (:y (first row)) (+ y-offset height)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
121 (into
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
122 []
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
123 (remove
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
124 nil?
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
125 (map
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
126 (fn [cell]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
127 (when
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
128 (and
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
129 (>= (:x cell) x-offset)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
130 (< (:x cell) (+ x-offset width)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
131 cell))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
132 row)))))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
133 grid))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
134
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
135 (defn get-surface
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
136 "Return, as a vector of vectors of cells represented as Clojure maps, a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
137 segment of surface from this `base-map` as modified by this
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
138 `noise-map` at this `cell-size` starting at this `x-offset` and `y-offset`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
139 and having this `width` and `height`.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
140
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
141 If `base-map` and `noise-map` are not supplied, the bindings of `*base-map*`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
142 and `*noise-map*` will be used, respectively.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
143
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
144 `base-map` and `noise-map` may be passed either as strings, assumed to be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
145 file paths of PNG files, or as MicroWorld style world arrays. It is assumed
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
146 that one pixel in `base-map` represents one square kilometre in the game
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
147 world. It is assumed that `cell-size`, `x-offset`, `y-offset`, `width` and
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
148 `height` are integer numbers of metres."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
149 ([cell-size x-offset y-offset width height]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
150 (get-surface *base-map* *noise-map* cell-size x-offset y-offset width height))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
151 ([base-map noise-map cell-size x-offset y-offset width height]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
152 (let [b (if (seq? base-map) base-map (scale-grid (apply-heightmap base-map) 1000))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
153 n (if (seq? noise-map) noise-map (apply-heightmap noise-map))]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 11 forms covered">
|
||||
154 (if (and (in-bounds? b x-offset y-offset)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 8 forms covered">
|
||||
155 (in-bounds? b (+ x-offset width) (+ y-offset height)))
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 1 forms covered">
|
||||
156 b ;; actually do stuff
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
157 (throw (Exception. "Surface out of bounds for map.")))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
158 )))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
159
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,119 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/world/location.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.world.location
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Functions dealing with location in the world."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [clojure.math.numeric-tower :refer [expt sqrt]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
004
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 ;; A 'location' value is a list comprising at most the x/y coordinate location
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 ;; and the ids of the settlement and region (possibly hierarchically) that contain
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ;; the location. If the x/y is not local to the home of the receiving agent, they
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ;; won't remember it and won't pass it on; if any of the ids are not interesting
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 ;; So location information will degrade progressively as the item is passed along.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 ;; It is assumed that the `:home` of a character is a location in this sense.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
012
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
013 (defn get-coords
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 "Return the coordinates in the game world of `location`, which may be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 1. A coordinate pair in the format {:x 5 :y 32};
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 2. A location, as discussed above;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 3. Any other gameworld object, having a `:location` property whose value
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 is one of the above."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 [location]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
020 (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
021 (empty? location) nil
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
022 (map? location)
|
||||
</span><br/>
|
||||
<span class="partial" title="1 out of 3 forms covered">
|
||||
023 (cond
|
||||
</span><br/>
|
||||
<span class="partial" title="13 out of 14 forms covered">
|
||||
024 (and (number? (:x location)) (number? (:y location)))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
025 location
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
026 (:location location)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
027 (:location location))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 :else
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
029 (get-coords (first (remove keyword? location)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
030
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
031 (defn distance-between
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 [location-1 location-2]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
033 (let [c1 (get-coords location-1)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
034 c2 (get-coords location-2)]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
035 (when
|
||||
</span><br/>
|
||||
<span class="partial" title="5 out of 6 forms covered">
|
||||
036 (and c1 c2)
|
||||
</span><br/>
|
||||
<span class="covered" title="23 out of 23 forms covered">
|
||||
037 (sqrt (+ (expt (- (:x c1) (:x c2)) 2) (expt (- (:y c1) (:y c2)) 2))))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/world/mw.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.world.mw
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Functions dealing with building a great game world from a MicroWorld world."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [clojure.math.numeric-tower :refer [expt sqrt]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [mw-engine.core :refer []]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [mw-engine.world :refer []]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
006
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ;; It's not at all clear to me yet what the workflow for getting a MicroWorld map into The Great Game, and whether it passes through Walkmap to get here. This file as currently written assumes it doesn't.
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,173 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/world/routes.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.world.routes
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Conceptual (plan level) routes, represented as tuples of location ids."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [cc.journeyman.the-great-game.utils :refer [cyclic?]]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
004
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
005 (defn find-routes
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 "Find routes from among these `routes` from `from`; if `to` is supplied,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 to `to`, by breadth-first search."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ([routes from]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
009 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
010 (fn [to] (cons from to))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
011 (remove
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
012 empty?
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
013 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
014 (fn [route]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
015 (remove
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
016 #(= from %)
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
017 (if (some #(= % from) route) route)))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
018 routes))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ([routes from to]
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
020 (let [steps (find-routes routes from)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
021 found (filter
|
||||
</span><br/>
|
||||
<span class="covered" title="10 out of 10 forms covered">
|
||||
022 (fn [step] (if (some #(= to %) step) step))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
023 steps)]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
024 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
025 (empty? found)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
026 (find-routes routes from to steps)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
027 found)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 ([routes from to steps]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
029 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
030 (not (empty? steps))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
031 (let [paths (remove
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
032 cyclic?
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
033 (mapcat
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
034 (fn [path]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
035 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
036 (fn [x] (concat path (rest x)))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
037 (find-routes routes (last path))))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
038 steps))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
039 found (filter
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
040 #(= (last %) to) paths)]
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
041 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
042 (empty? found)
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
043 (find-routes routes from to paths)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
044 found)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
045
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
046 (defn find-route
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 "Find a single route from `from` to `to` in this `world-or-routes`, which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 may be either a world as defined in [[the-great-game.world.world]] or else
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 a sequence of tuples of keywords."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 [world-or-routes from to]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
051 (first
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
052 (find-routes
|
||||
</span><br/>
|
||||
<span class="partial" title="7 out of 8 forms covered">
|
||||
053 (or (:routes world-or-routes) world-or-routes)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
054 from
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
055 to)))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
125
docs/cloverage/cc/journeyman/the_great_game/world/run.clj.html
Normal file
125
docs/cloverage/cc/journeyman/the_great_game/world/run.clj.html
Normal file
|
@ -0,0 +1,125 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/world/run.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.world.run
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Run the whole simulation"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 (:require [environ.core :refer [env]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 [taoensso.timbre :as timbre]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [taoensso.timbre.appenders.3rd-party.rotor :as rotor]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [cc.journeyman.the-great-game.gossip.gossip :as g]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [cc.journeyman.the-great-game.merchants.merchants :as m]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [cc.journeyman.the-great-game.merchants.markets :as k]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [cc.journeyman.the-great-game.world.world :as w]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
010
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
011 (defn init
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ([]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
013 (init {}))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ([config]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
015 (timbre/merge-config!
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
016 {:appenders
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
017 {:rotor (rotor/rotor-appender
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 6 forms covered">
|
||||
018 {:path "the-great-game.log"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
019 :max-size (* 512 1024)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 :backlog 10})}
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 9 forms covered">
|
||||
021 :level (or
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
022 (:log-level config)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
023 (if (env :dev) :debug)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 :info)})))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
025
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
026 (defn run
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 "The pipeline to run the simulation each game day. Returns a world like
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 this world, with all the various active elements updated. The optional
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 `date` argument, if supplied, is set as the `:date` of the returned world."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 ([world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
031 (g/run
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
032 (m/run
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
033 (k/run
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
034 (w/run world)))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 ([world date]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
036 (g/run
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
037 (m/run
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
038 (k/run
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 4 forms covered">
|
||||
039 (w/run world date))))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
584
docs/cloverage/cc/journeyman/the_great_game/world/world.clj.html
Normal file
584
docs/cloverage/cc/journeyman/the_great_game/world/world.clj.html
Normal file
|
@ -0,0 +1,584 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../../coverage.css"/> <title> cc/journeyman/the_great_game/world/world.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns cc.journeyman.the-great-game.world.world
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 "Access to data about the world")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
003
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 ;;; The world has to work either as map or a database. Initially, and for
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 ;;; unit tests, I'll use a map; later, there will be a database. But the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 ;;; API needs to be agnostic, so that heirarchies which interact with
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 ;;; `world` don't have to know which they've got - as far as they're concerned
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 ;;; it's just a handle.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
009
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
010 (def default-world
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
011 "A basic world for testing concepts"
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
012 {:date 0 ;; the age of this world in game days
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 :cities
|
||||
</span><br/>
|
||||
<span class="covered" title="8 out of 8 forms covered">
|
||||
014 {:aberdeen
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
015 {:id :aberdeen
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 :supplies
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;; `supplies` is the quantity of each commodity added to the stock
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;; each game day. If the price in the market is lower than 1 (the
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;; cost of production of a unit) no goods will be added.
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
020 {:fish 10
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 :leather 5}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
022 :demands
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
023 ;; `stock` is the quantity of each commodity in the market at any
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 ;; given time. It is adjusted for production and consumption at
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 ;; the end of each game day.
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
026 {:iron 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
027 :cloth 10
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
028 :whisky 10}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 :port true
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
030 :prices
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
031 ;; `prices`: the current price (both buying and selling, for simplicity)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
032 ;; of each commodity in the market. Updated each game day based on current
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 ;; stock.
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
034 {:cloth 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 :fish 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 :leather 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 :iron 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 :whisky 1}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 :stock
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 ;; `stock` is the quantity of each commodity in the market at any
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 ;; given time. It is adjusted for production and consumption at
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 ;; the end of each game day.
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
043 {:cloth 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
044 :fish 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
045 :leather 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 :iron 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
047 :whisky 0}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
048 :cash 100}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
049 :buckie
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
050 {:id :buckie
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 :supplies
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
052 {:fish 20}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 :demands
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
054 {:cloth 5
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
055 :leather 3
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
056 :whisky 5
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 :iron 1}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
058 :port true
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
059 :prices {:cloth 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
060 :fish 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
061 :leather 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
062 :iron 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
063 :whisky 1}
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
064 :stock {:cloth 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 :fish 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
066 :leather 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
067 :iron 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 :whisky 0}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
069 :cash 100}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
070 :callander
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
071 {:id :callander
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
072 :supplies {:leather 20}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
073 :demands
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
074 {:cloth 5
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
075 :fish 3
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
076 :whisky 5
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 :iron 1}
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
078 :prices {:cloth 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
079 :fish 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
080 :leather 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
081 :iron 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
082 :whisky 1}
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
083 :stock {:cloth 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
084 :fish 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 :leather 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
086 :iron 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
087 :whisky 0}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
088 :cash 100}
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
089 :dundee {:id :dundee}
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
090 :edinburgh {:id :dundee}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 :falkirk
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
092 {:id :falkirk
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
093 :supplies {:iron 10}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 :demands
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
095 {:cloth 5
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
096 :leather 3
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
097 :whisky 5
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
098 :fish 10}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 :port true
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
100 :prices {:cloth 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 :fish 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102 :leather 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
103 :iron 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
104 :whisky 1}
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
105 :stock {:cloth 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
106 :fish 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
107 :leather 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
108 :iron 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 :whisky 0}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
110 :cash 100}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
111 :glasgow
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
112 {:id :glasgow
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
113 :supplies {:whisky 10}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
114 :demands
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
115 {:cloth 5
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
116 :leather 3
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
117 :iron 5
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
118 :fish 10}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
119 :port true
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
120 :prices {:cloth 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
121 :fish 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
122 :leather 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
123 :iron 1
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
124 :whisky 1}
|
||||
</span><br/>
|
||||
<span class="covered" title="11 out of 11 forms covered">
|
||||
125 :stock {:cloth 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
126 :fish 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
127 :leather 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
128 :iron 0
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
129 :whisky 0}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
130 :cash 100}}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
131 :merchants
|
||||
</span><br/>
|
||||
<span class="covered" title="20 out of 20 forms covered">
|
||||
132 {:archie {:id :archie
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
133 :home :aberdeen :location :aberdeen :cash 100 :capacity 10
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
134 :known-prices {}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
135 :stock {}}
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
136 :belinda {:id :belinda
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
137 :home :buckie :location :buckie :cash 100 :capacity 10
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
138 :known-prices {}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
139 :stock {}}
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
140 :callum {:id :callum
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
141 :home :callander :location :calander :cash 100 :capacity 10
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
142 :known-prices {}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
143 :stock {}}
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
144 :deirdre {:id :deidre
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
145 :home :dundee :location :dundee :cash 100 :capacity 10
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
146 :known-prices {}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
147 :stock {}}
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
148 :euan {:id :euan
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
149 :home :edinbirgh :location :edinburgh :cash 100 :capacity 10
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
150 :known-prices {}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
151 :stock {}}
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
152 :fiona {:id :fiona
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
153 :home :falkirk :location :falkirk :cash 100 :capacity 10
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
154 :known-prices {}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
155 :stock {}}}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
156 :routes
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
157 ;; all routes can be traversed in either direction and are assumed to
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
158 ;; take the same amount of time.
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
159 [[:aberdeen :buckie]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
160 [:aberdeen :dundee]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
161 [:callander :glasgow]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
162 [:dundee :callander]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
163 [:dundee :edinburgh]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
164 [:dundee :falkirk]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
165 [:edinburgh :falkirk]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
166 [:falkirk :glasgow]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
167 :commodities
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
168 ;; cost of commodities is expressed in person/days;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
169 ;; weight in packhorse loads. Transport in this model
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
170 ;; is all overland; you don't take bulk cargoes overland
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
171 ;; in this period, it's too expensive.
|
||||
</span><br/>
|
||||
<span class="covered" title="13 out of 13 forms covered">
|
||||
172 {:cloth {:id :cloth :cost 1 :weight 0.25}
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
173 :fish {:id :fish :cost 1 :weight 1}
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
174 :leather {:id :leather :cost 1 :weight 0.5}
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
175 :whisky {:id :whisky :cost 1 :weight 0.1}
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
176 :iron {:id :iron :cost 1 :weight 10}}})
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
177
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
178 (defn actual-price
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
179 "Find the actual current price of this `commodity` in this `city` given
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
180 this `world`. **NOTE** that merchants can only know the actual prices in
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
181 the city in which they are currently located."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
182 [world commodity city]
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
183 (-> world :cities city :prices commodity))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
184
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
185 (defn run
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
186 "Return a world like this `world` with only the `:date` to this `date`
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
187 (or id `date` not supplied, the current value incremented by one). For
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
188 running other aspects of the simulation, see [[the-great-game.world.run]]."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
189 ([world]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 12 forms covered">
|
||||
190 (run world (inc (or (:date world) 0))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
191 ([world date]
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
192 (assoc world :date date)))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
|
@ -15,44 +15,113 @@
|
|||
<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/gossip/gossip.clj.html">the-great-game.gossip.gossip</a></td><td class="with-bar"><div class="covered"
|
||||
<td><a href="cc/journeyman/the_great_game/agent/agent.clj.html">cc.journeyman.the-great-game.agent.agent</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 3 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 3 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-number">45</td><td class="with-number">5</td><td class="with-number">3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="cc/journeyman/the_great_game/buildings/module.clj.html">cc.journeyman.the-great-game.buildings.module</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">81</td><td class="with-number">6</td><td class="with-number">2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="cc/journeyman/the_great_game/buildings/rectangular.clj.html">cc.journeyman.the-great-game.buildings.rectangular</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:74.64788732394366%;
|
||||
float:left;"> 106 </div><div class="not-covered"
|
||||
style="width:25.35211267605634%;
|
||||
float:left;"> 36 </div></td>
|
||||
<td class="with-number">74.65 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:77.41935483870968%;
|
||||
float:left;"> 24 </div><div class="partial"
|
||||
style="width:3.225806451612903%;
|
||||
float:left;"> 1 </div><div class="not-covered"
|
||||
style="width:19.35483870967742%;
|
||||
float:left;"> 6 </div></td>
|
||||
<td class="with-number">80.65 %</td>
|
||||
<td class="with-number">150</td><td class="with-number">25</td><td class="with-number">31</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="cc/journeyman/the_great_game/gossip/gossip.clj.html">cc.journeyman.the-great-game.gossip.gossip</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:4.62962962962963%;
|
||||
float:left;"> 5 </div><div class="not-covered"
|
||||
style="width:95.37037037037037%;
|
||||
float:left;"> 103 </div></td>
|
||||
<td class="with-number">4.63 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:12.820512820512821%;
|
||||
style="width:12.5%;
|
||||
float:left;"> 5 </div><div class="not-covered"
|
||||
style="width:87.17948717948718%;
|
||||
float:left;"> 34 </div></td>
|
||||
<td class="with-number">12.82 %</td>
|
||||
<td class="with-number">66</td><td class="with-number">5</td><td class="with-number">39</td>
|
||||
style="width:87.5%;
|
||||
float:left;"> 35 </div></td>
|
||||
<td class="with-number">12.50 %</td>
|
||||
<td class="with-number">73</td><td class="with-number">7</td><td class="with-number">40</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.80155642023347%;
|
||||
float:left;"> 477 </div><div class="not-covered"
|
||||
style="width:7.198443579766537%;
|
||||
float:left;"> 37 </div></td>
|
||||
<td class="with-number">92.80 %</td>
|
||||
<td><a href="cc/journeyman/the_great_game/gossip/news_items.clj.html">cc.journeyman.the-great-game.gossip.news-items</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:77.15582450832072%;
|
||||
float:left;"> 510 </div><div class="not-covered"
|
||||
style="width:22.844175491679273%;
|
||||
float:left;"> 151 </div></td>
|
||||
<td class="with-number">77.16 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:88.07339449541284%;
|
||||
float:left;"> 96 </div><div class="partial"
|
||||
style="width:7.339449541284404%;
|
||||
style="width:72.66187050359713%;
|
||||
float:left;"> 101 </div><div class="partial"
|
||||
style="width:5.755395683453237%;
|
||||
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>
|
||||
style="width:21.58273381294964%;
|
||||
float:left;"> 30 </div></td>
|
||||
<td class="with-number">78.42 %</td>
|
||||
<td class="with-number">345</td><td class="with-number">41</td><td class="with-number">139</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:96.46464646464646%;
|
||||
float:left;"> 191 </div><div class="not-covered"
|
||||
style="width:3.5353535353535355%;
|
||||
<td><a href="cc/journeyman/the_great_game/holdings/holding.clj.html">cc.journeyman.the-great-game.holdings.holding</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:14.285714285714286%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:85.71428571428571%;
|
||||
float:left;"> 18 </div></td>
|
||||
<td class="with-number">14.29 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:42.857142857142854%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:57.142857142857146%;
|
||||
float:left;"> 4 </div></td>
|
||||
<td class="with-number">42.86 %</td>
|
||||
<td class="with-number">46</td><td class="with-number">3</td><td class="with-number">7</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="cc/journeyman/the_great_game/location/location.clj.html">cc.journeyman.the-great-game.location.location</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:15.384615384615385%;
|
||||
float:left;"> 4 </div><div class="not-covered"
|
||||
style="width:84.61538461538461%;
|
||||
float:left;"> 22 </div></td>
|
||||
<td class="with-number">15.38 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:28.571428571428573%;
|
||||
float:left;"> 4 </div><div class="not-covered"
|
||||
style="width:71.42857142857143%;
|
||||
float:left;"> 10 </div></td>
|
||||
<td class="with-number">28.57 %</td>
|
||||
<td class="with-number">45</td><td class="with-number">7</td><td class="with-number">14</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="cc/journeyman/the_great_game/merchants/markets.clj.html">cc.journeyman.the-great-game.merchants.markets</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:96.48241206030151%;
|
||||
float:left;"> 192 </div><div class="not-covered"
|
||||
style="width:3.5175879396984926%;
|
||||
float:left;"> 7 </div></td>
|
||||
<td class="with-number">96.46 %</td>
|
||||
<td class="with-number">96.48 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:93.18181818181819%;
|
||||
float:left;"> 41 </div><div class="partial"
|
||||
|
@ -64,7 +133,7 @@
|
|||
<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"
|
||||
<td><a href="cc/journeyman/the_great_game/merchants/merchant_utils.clj.html">cc.journeyman.the-great-game.merchants.merchant-utils</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:65.4485049833887%;
|
||||
float:left;"> 197 </div><div class="not-covered"
|
||||
style="width:34.5514950166113%;
|
||||
|
@ -81,12 +150,12 @@
|
|||
<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"
|
||||
style="width:2.816901408450704%;
|
||||
<td><a href="cc/journeyman/the_great_game/merchants/merchants.clj.html">cc.journeyman.the-great-game.merchants.merchants</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:2.73972602739726%;
|
||||
float:left;"> 2 </div><div class="not-covered"
|
||||
style="width:97.1830985915493%;
|
||||
float:left;"> 69 </div></td>
|
||||
<td class="with-number">2.82 %</td>
|
||||
style="width:97.26027397260275%;
|
||||
float:left;"> 71 </div></td>
|
||||
<td class="with-number">2.74 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:12.5%;
|
||||
float:left;"> 2 </div><div class="not-covered"
|
||||
|
@ -96,7 +165,7 @@
|
|||
<td class="with-number">28</td><td class="with-number">3</td><td class="with-number">16</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="the_great_game/merchants/planning.clj.html">the-great-game.merchants.planning</a></td><td class="with-bar"><div class="covered"
|
||||
<td><a href="cc/journeyman/the_great_game/merchants/planning.clj.html">cc.journeyman.the-great-game.merchants.planning</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:89.27335640138408%;
|
||||
float:left;"> 258 </div><div class="not-covered"
|
||||
style="width:10.726643598615917%;
|
||||
|
@ -113,12 +182,12 @@
|
|||
<td class="with-number">159</td><td class="with-number">11</td><td class="with-number">85</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="the_great_game/merchants/strategies/simple.clj.html">the-great-game.merchants.strategies.simple</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:0.8264462809917356%;
|
||||
<td><a href="cc/journeyman/the_great_game/merchants/strategies/simple.clj.html">cc.journeyman.the-great-game.merchants.strategies.simple</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:0.8156606851549756%;
|
||||
float:left;"> 5 </div><div class="not-covered"
|
||||
style="width:99.17355371900827%;
|
||||
float:left;"> 600 </div></td>
|
||||
<td class="with-number">0.83 %</td>
|
||||
style="width:99.18433931484502%;
|
||||
float:left;"> 608 </div></td>
|
||||
<td class="with-number">0.82 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:4.032258064516129%;
|
||||
float:left;"> 5 </div><div class="not-covered"
|
||||
|
@ -128,7 +197,7 @@
|
|||
<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"
|
||||
<td><a href="cc/journeyman/the_great_game/objects/container.clj.html">cc.journeyman.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>
|
||||
|
@ -139,48 +208,86 @@
|
|||
<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%;
|
||||
<td><a href="cc/journeyman/the_great_game/objects/game_object.clj.html">cc.journeyman.the-great-game.objects.game-object</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:50.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>
|
||||
style="width:50.0%;
|
||||
float:left;"> 3 </div></td>
|
||||
<td class="with-number">50.00 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:60.0%;
|
||||
style="width:50.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>
|
||||
style="width:50.0%;
|
||||
float:left;"> 3 </div></td>
|
||||
<td class="with-number">50.00 %</td>
|
||||
<td class="with-number">21</td><td class="with-number">2</td><td class="with-number">6</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.5850622406639%;
|
||||
float:left;"> 240 </div><div class="not-covered"
|
||||
style="width:0.4149377593360996%;
|
||||
float:left;"> 1 </div></td>
|
||||
<td class="with-number">99.59 %</td>
|
||||
<td><a href="cc/journeyman/the_great_game/playroom.clj.html">cc.journeyman.the-great-game.playroom</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:56.92307692307692%;
|
||||
float:left;"> 222 </div><div class="not-covered"
|
||||
style="width:43.07692307692308%;
|
||||
float:left;"> 168 </div></td>
|
||||
<td class="with-number">56.92 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:98.33333333333333%;
|
||||
float:left;"> 59 </div><div class="partial"
|
||||
style="width:23.529411764705884%;
|
||||
float:left;"> 4 </div><div class="partial"
|
||||
style="width:17.647058823529413%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:58.8235294117647%;
|
||||
float:left;"> 10 </div></td>
|
||||
<td class="with-number">41.18 %</td>
|
||||
<td class="with-number">72</td><td class="with-number">12</td><td class="with-number">17</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="cc/journeyman/the_great_game/time.clj.html">cc.journeyman.the-great-game.time</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:97.9253112033195%;
|
||||
float:left;"> 236 </div><div class="not-covered"
|
||||
style="width:2.074688796680498%;
|
||||
float:left;"> 5 </div></td>
|
||||
<td class="with-number">97.93 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:96.66666666666667%;
|
||||
float:left;"> 58 </div><div class="partial"
|
||||
style="width:1.6666666666666667%;
|
||||
float:left;"> 1 </div><div class="not-covered"
|
||||
style="width:1.6666666666666667%;
|
||||
float:left;"> 1 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-number">98.33 %</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%;
|
||||
float:left;"> 69 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td><a href="cc/journeyman/the_great_game/utils.clj.html">cc.journeyman.the-great-game.utils</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:85.4368932038835%;
|
||||
float:left;"> 88 </div><div class="not-covered"
|
||||
style="width:14.563106796116505%;
|
||||
float:left;"> 15 </div></td>
|
||||
<td class="with-number">85.44 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 19 </div></td>
|
||||
<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>
|
||||
style="width:92.5925925925926%;
|
||||
float:left;"> 25 </div><div class="not-covered"
|
||||
style="width:7.407407407407407%;
|
||||
float:left;"> 2 </div></td>
|
||||
<td class="with-number">92.59 %</td>
|
||||
<td class="with-number">61</td><td class="with-number">8</td><td class="with-number">27</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"
|
||||
<td><a href="cc/journeyman/the_great_game/world/heightmap.clj.html">cc.journeyman.the-great-game.world.heightmap</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:3.5947712418300655%;
|
||||
float:left;"> 11 </div><div class="not-covered"
|
||||
style="width:96.40522875816994%;
|
||||
float:left;"> 295 </div></td>
|
||||
<td class="with-number">3.59 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:12.67605633802817%;
|
||||
float:left;"> 9 </div><div class="not-covered"
|
||||
style="width:87.32394366197182%;
|
||||
float:left;"> 62 </div></td>
|
||||
<td class="with-number">12.68 %</td>
|
||||
<td class="with-number">159</td><td class="with-number">16</td><td class="with-number">71</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="cc/journeyman/the_great_game/world/location.clj.html">cc.journeyman.the-great-game.world.location</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:87.95180722891567%;
|
||||
float:left;"> 73 </div><div class="not-covered"
|
||||
style="width:12.048192771084338%;
|
||||
|
@ -197,7 +304,18 @@
|
|||
<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"
|
||||
<td><a href="cc/journeyman/the_great_game/world/mw.clj.html">cc.journeyman.the-great-game.world.mw</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 1 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:100.0%;
|
||||
float:left;"> 1 </div></td>
|
||||
<td class="with-number">100.00 %</td>
|
||||
<td class="with-number">7</td><td class="with-number">1</td><td class="with-number">1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="cc/journeyman/the_great_game/world/routes.clj.html">cc.journeyman.the-great-game.world.routes</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:99.19354838709677%;
|
||||
float:left;"> 123 </div><div class="not-covered"
|
||||
style="width:0.8064516129032258%;
|
||||
|
@ -212,7 +330,7 @@
|
|||
<td class="with-number">55</td><td class="with-number">2</td><td class="with-number">42</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="the_great_game/world/run.clj.html">the-great-game.world.run</a></td><td class="with-bar"><div class="covered"
|
||||
<td><a href="cc/journeyman/the_great_game/world/run.clj.html">cc.journeyman.the-great-game.world.run</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:5.0%;
|
||||
float:left;"> 3 </div><div class="not-covered"
|
||||
style="width:95.0%;
|
||||
|
@ -227,7 +345,7 @@
|
|||
<td class="with-number">39</td><td class="with-number">2</td><td class="with-number">20</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="the_great_game/world/world.clj.html">the-great-game.world.world</a></td><td class="with-bar"><div class="covered"
|
||||
<td><a href="cc/journeyman/the_great_game/world/world.clj.html">cc.journeyman.the-great-game.world.world</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:96.10983981693364%;
|
||||
float:left;"> 420 </div><div class="not-covered"
|
||||
style="width:3.8901601830663615%;
|
||||
|
@ -243,9 +361,9 @@
|
|||
</tr>
|
||||
<tr><td>Totals:</td>
|
||||
<td class="with-bar"></td>
|
||||
<td class="with-number">66.55 %</td>
|
||||
<td class="with-number">58.91 %</td>
|
||||
<td class="with-bar"></td>
|
||||
<td class="with-number">68.63 %</td>
|
||||
<td class="with-number">61.63 %</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
|
File diff suppressed because one or more lines are too long
20
docs/codox/API_Spec.html
Normal file
20
docs/codox/API_Spec.html
Normal file
File diff suppressed because one or more lines are too long
29
docs/codox/Appraisal.html
Normal file
29
docs/codox/Appraisal.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
66
docs/codox/Biomes_and_ecology.html
Normal file
66
docs/codox/Biomes_and_ecology.html
Normal file
File diff suppressed because one or more lines are too long
8
docs/codox/Building_on_microworld.html
Normal file
8
docs/codox/Building_on_microworld.html
Normal file
File diff suppressed because one or more lines are too long
36
docs/codox/Canonical-dictionary.html
Normal file
36
docs/codox/Canonical-dictionary.html
Normal file
File diff suppressed because one or more lines are too long
19
docs/codox/Division_of_tasks_between_server_and_client.html
Normal file
19
docs/codox/Division_of_tasks_between_server_and_client.html
Normal file
File diff suppressed because one or more lines are too long
45
docs/codox/Dynamic-consequences.html
Normal file
45
docs/codox/Dynamic-consequences.html
Normal file
File diff suppressed because one or more lines are too long
36
docs/codox/Economy.html
Normal file
36
docs/codox/Economy.html
Normal file
File diff suppressed because one or more lines are too long
13
docs/codox/Further-reading.html
Normal file
13
docs/codox/Further-reading.html
Normal file
File diff suppressed because one or more lines are too long
7
docs/codox/Game-engine-integration.html
Normal file
7
docs/codox/Game-engine-integration.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
46
docs/codox/Genetic-buildings.html
Normal file
46
docs/codox/Genetic-buildings.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
42
docs/codox/MVP-Roadmap.html
Normal file
42
docs/codox/MVP-Roadmap.html
Normal file
File diff suppressed because one or more lines are too long
18
docs/codox/Modelling_democracy_and_morale.html
Normal file
18
docs/codox/Modelling_democracy_and_morale.html
Normal file
File diff suppressed because one or more lines are too long
8
docs/codox/Modelling_trading_cost_and_risk.html
Normal file
8
docs/codox/Modelling_trading_cost_and_risk.html
Normal file
File diff suppressed because one or more lines are too long
6
docs/codox/My-setting.html
Normal file
6
docs/codox/My-setting.html
Normal file
File diff suppressed because one or more lines are too long
32
docs/codox/Naming-of-characters.html
Normal file
32
docs/codox/Naming-of-characters.html
Normal file
File diff suppressed because one or more lines are too long
21
docs/codox/Not_my_problem.html
Normal file
21
docs/codox/Not_my_problem.html
Normal file
File diff suppressed because one or more lines are too long
17
docs/codox/On-dying.html
Normal file
17
docs/codox/On-dying.html
Normal file
File diff suppressed because one or more lines are too long
45
docs/codox/On-sex-and-sexual-violence.html
Normal file
45
docs/codox/On-sex-and-sexual-violence.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
24
docs/codox/Roadmap.html
Normal file
24
docs/codox/Roadmap.html
Normal file
File diff suppressed because one or more lines are too long
41
docs/codox/Sandbox.html
Normal file
41
docs/codox/Sandbox.html
Normal file
File diff suppressed because one or more lines are too long
52
docs/codox/Selecting_Character.html
Normal file
52
docs/codox/Selecting_Character.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
37
docs/codox/Sexual-dimorphism.html
Normal file
37
docs/codox/Sexual-dimorphism.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
43
docs/codox/Things_Voice_Interaction_Enables.html
Normal file
43
docs/codox/Things_Voice_Interaction_Enables.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
8
docs/codox/building_on_microworld.html
Normal file
8
docs/codox/building_on_microworld.html
Normal file
File diff suppressed because one or more lines are too long
24
docs/codox/cc.journeyman.the-great-game.agent.agent.html
Normal file
24
docs/codox/cc.journeyman.the-great-game.agent.agent.html
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue