Added Version in simple ANSI-C

This commit is contained in:
kingsley
2021-10-10 17:26:32 +11:00
parent 0f5665fae4
commit 13f355f5fa
2 changed files with 498 additions and 0 deletions

23
38 Fur Trader/c/README.md Normal file
View File

@@ -0,0 +1,23 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [ANSI-C](https://en.wikipedia.org/wiki/ANSI_C)
##### Translator Notes:
I tried to preserve as much of the original layout and flow of the code
as possible. However I did use enumerated types for the Fort numbers
and Fur types. I think this was certainly a change for the better, and
makes the code much easier to read.
I also tried to minimise the use of pointers, and stuck with old-school
C formatting, because you never know how old the compiler is.
Interestingly the code seems to have a bug around the prices of Fox Furs.
The commodity-rate for these is stored in the variable `D1`, however some
paths through the code do not set this price. So there was a chance of
using this uninitialised, or whatever the previous loop set. I don't
think this was the original authors intent. So I preserved the original flow
of the code (using the previous `D1` value), but also catching the
uninitialised path, and assigning a "best guess" value.
krt@krt.com.au 2020-10-10

475
38 Fur Trader/c/furtrader.c Executable file
View File

@@ -0,0 +1,475 @@
/*
* Ported from furtrader.bas to ANSI C (C99) by krt@krt.com.au
*
* compile with:
* gcc -g -Wall -Werror furtrader.c -o furtrader
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* Constants */
#define FUR_TYPE_COUNT 4
#define FUR_MINK 0
#define FUR_BEAVER 1
#define FUR_ERMINE 2
#define FUR_FOX 3
#define MAX_FURS 190
const char *FUR_NAMES[FUR_TYPE_COUNT] = { "MINK", "BEAVER", "ERMINE", "FOX" };
#define FORT_TYPE_COUNT 3
#define FORT_MONTREAL 1
#define FORT_QUEBEC 2
#define FORT_NEWYORK 3
const char *FORT_NAMES[FORT_TYPE_COUNT] = { "HOCHELAGA (MONTREAL)", "STADACONA (QUEBEC)", "NEW YORK" };
/* Print the words at the specified column */
void printAtColumn( int column, const char *words )
{
int i;
for ( i=0; i<column; i++ )
printf( " " );
printf( "%s\n", words );
}
/* trivial function to output a line with a \n */
void print( const char *words )
{
printf( "%s\n", words );
}
/* Show the player the introductory message */
void showIntroduction()
{
print( "YOU ARE THE LEADER OF A FRENCH FUR TRADING EXPEDITION IN " );
print( "1776 LEAVING THE LAKE ONTARIO AREA TO SELL FURS AND GET" );
print( "SUPPLIES FOR THE NEXT YEAR. YOU HAVE A CHOICE OF THREE" );
print( "FORTS AT WHICH YOU MAY TRADE. THE COST OF SUPPLIES" );
print( "AND THE AMOUNT YOU RECEIVE FOR YOUR FURS WILL DEPEND" );
print( "ON THE FORT THAT YOU CHOOSE." );
print( "" );
}
/*
* Prompt the user for input.
* When input is given, try to conver it to an integer
* return the integer converted value or 0 on error
*/
int getNumericInput()
{
int result = -1;
char buffer[64]; /* somewhere to store user input */
char *endstr;
while ( result == -1 )
{
printf( ">> " ); /* prompt the user */
fgets( buffer, sizeof( buffer ), stdin ); /* read from the console into the buffer */
result = (int)strtol( buffer, &endstr, 10 ); /* only simple error checking */
if ( endstr == buffer ) /* was the string -> integer ok? */
result = -1;
}
return result;
}
/*
* Prompt the user for YES/NO input.
* When input is given, try to work out if it's YES, Yes, yes, Y, etc.
* And convert to a single upper-case letter
* Returns a character of 'Y' or 'N'.
*/
char getYesOrNo()
{
char result = '!';
char buffer[64]; /* somewhere to store user input */
while ( !( result == 'Y' || result == 'N' ) ) /* While the answer was not Yes or No */
{
print( "ANSWER YES OR NO" );
printf( ">> " );
fgets( buffer, sizeof( buffer ), stdin ); /* read from the console into the buffer */
if ( buffer[0] == 'Y' || buffer[0] == 'y' )
result = 'Y';
else if ( buffer[0] == 'N' || buffer[0] == 'n' )
result = 'N';
}
return result;
}
/*
* Show the player the choices of Fort, get their input, if the
* input is a valid choice (1,2,3) return it, otherwise keep
* prompting the user.
*/
int getFortChoice()
{
int result = 0;
while ( result == 0 )
{
print( "" );
print( "YOU MAY TRADE YOUR FURS AT FORT 1, FORT 2," );
print( "OR FORT 3. FORT 1 IS FORT HOCHELAGA (MONTREAL)" );
print( "AND IS UNDER THE PROTECTION OF THE FRENCH ARMY." );
print( "FORT 2 IS FORT STADACONA (QUEBEC) AND IS UNDER THE" );
print( "PROTECTION OF THE FRENCH ARMY. HOWEVER, YOU MUST" );
print( "MAKE A PORTAGE AND CROSS THE LACHINE RAPIDS." );
print( "FORT 3 IS FORT NEW YORK AND IS UNDER DUTCH CONTROL." );
print( "YOU MUST CROSS THROUGH IROQUOIS LAND." );
print( "ANSWER 1, 2, OR 3." );
result = getNumericInput(); /* get input from the player */
}
return result;
}
/*
* Print the description for the fort
*/
void showFortComment( int which_fort )
{
print( "" );
if ( which_fort == FORT_MONTREAL )
{
print( "YOU HAVE CHOSEN THE EASIEST ROUTE. HOWEVER, THE FORT" );
print( "IS FAR FROM ANY SEAPORT. THE VALUE" );
print( "YOU RECEIVE FOR YOUR FURS WILL BE LOW AND THE COST" );
print( "OF SUPPLIES HIGHER THAN AT FORTS STADACONA OR NEW YORK." );
}
else if ( which_fort == FORT_QUEBEC )
{
print( "YOU HAVE CHOSEN A HARD ROUTE. IT IS, IN COMPARSION," );
print( "HARDER THAN THE ROUTE TO HOCHELAGA BUT EASIER THAN" );
print( "THE ROUTE TO NEW YORK. YOU WILL RECEIVE AN AVERAGE VALUE" );
print( "FOR YOUR FURS AND THE COST OF YOUR SUPPLIES WILL BE AVERAGE." );
}
else if ( which_fort == FORT_NEWYORK )
{
print( "YOU HAVE CHOSEN THE MOST DIFFICULT ROUTE. AT" );
print( "FORT NEW YORK YOU WILL RECEIVE THE HIGHEST VALUE" );
print( "FOR YOUR FURS. THE COST OF YOUR SUPPLIES" );
print( "WILL BE LOWER THAN AT ALL THE OTHER FORTS." );
}
else
{
printf( "Internal error #1, fort %d does not exist\n", which_fort );
exit( 1 ); /* you have a bug */
}
print( "" );
}
/*
* Prompt the player for how many of each fur type they want.
* Accept numeric inputs, re-prompting on incorrect input values
*/
void getFursPurchase( int *furs )
{
int i;
printf( "YOUR %d FURS ARE DISTRIBUTED AMONG THE FOLLOWING\n", FUR_TYPE_COUNT );
print( "KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX." );
print( "" );
for ( i=0; i<FUR_TYPE_COUNT; i++ )
{
printf( "HOW MANY %s DO YOU HAVE\n", FUR_NAMES[i] );
furs[i] = getNumericInput();
}
}
/*
* (Re)Set the player's inventory to zero
*/
void zeroInventory( int *player_fur_count )
{
int i;
for ( i=0; i<FUR_TYPE_COUNT; i++ )
{
player_fur_count[i] = 0;
}
}
/*
* Tally the player's inventory
*/
int sumInventory( int *player_fur_count )
{
int result = 0;
int i;
for ( i=0; i<FUR_TYPE_COUNT; i++ )
{
result += player_fur_count[i];
}
return result;
}
/*
* Return a random number between a & b
* Ref: https://stackoverflow.com/a/686376/1730895
*/
float randomAB(float a, float b)
{
return ((b - a) * ((float)rand() / (float)RAND_MAX)) + a;
}
/* Random floating point number between 0 and 1 */
float randFloat()
{
return randomAB( 0, 1 );
}
/* States to allow switching in main game-loop */
#define STATE_STARTING 1
#define STATE_CHOOSING_FORT 2
#define STATE_TRAVELLING 3
#define STATE_TRADING 4
int main( void )
{
/* variables for storing player's status */
float player_funds = 0; /* no money */
int player_furs[FUR_TYPE_COUNT] = { 0, 0, 0, 0 }; /* no furs */
/* player input holders */
char yes_or_no;
int event_picker;
int which_fort;
/* what part of the game is in play */
int game_state = STATE_STARTING;
/* commodity prices */
float mink_price = -1;
float beaver_price = -1;
float ermine_price = -1;
float fox_price = -1; /* sometimes this takes the "last" price (probably this was a bug) */
float mink_value;
float beaver_value;
float ermine_value;
float fox_value; /* for calculating sales results */
srand( time( NULL ) ); /* seed the random number generator */
printAtColumn( 31, "FUR TRADER" );
printAtColumn( 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" );
printAtColumn( 15, "(Ported to ANSI-C Oct 2012 krt@krt.com.au)" );
print( "\n\n\n" );
/* Loop forever until the player asks to quit */
while ( 1 )
{
if ( game_state == STATE_STARTING )
{
showIntroduction();
player_funds = 600; /* Initial player start money */
zeroInventory( player_furs ); /* Player fur inventory */
print( "DO YOU WISH TO TRADE FURS?" );
yes_or_no = getYesOrNo();
if ( yes_or_no == 'N' )
exit( 0 ); /* STOP */
game_state = STATE_TRADING;
}
else if ( game_state == STATE_TRADING )
{
print( "" );
printf( "YOU HAVE $ %1.2f IN SAVINGS\n", player_funds );
printf( "AND %d FURS TO BEGIN THE EXPEDITION\n", MAX_FURS );
getFursPurchase( player_furs );
if ( sumInventory( player_furs ) > MAX_FURS )
{
print( "" );
print( "YOU MAY NOT HAVE THAT MANY FURS." );
print( "DO NOT TRY TO CHEAT. I CAN ADD." );
print( "YOU MUST START AGAIN." );
print( "" );
game_state = STATE_STARTING; /* T/N: Wow, harsh. */
}
else
{
game_state = STATE_CHOOSING_FORT;
}
}
else if ( game_state == STATE_CHOOSING_FORT )
{
which_fort = getFortChoice();
showFortComment( which_fort );
print( "DO YOU WANT TO TRADE AT ANOTHER FORT?" );
yes_or_no = getYesOrNo();
if ( yes_or_no == 'N' )
game_state = STATE_TRAVELLING;
}
else if ( game_state == STATE_TRAVELLING )
{
print( "" );
if ( which_fort == FORT_MONTREAL )
{
mink_price = ( ( 0.2 * randFloat() + 0.70 ) * 100 + 0.5 ) / 100;
ermine_price = ( ( 0.2 * randFloat() + 0.65 ) * 100 + 0.5 ) / 100;
beaver_price = ( ( 0.2 * randFloat() + 0.75 ) * 100 + 0.5 ) / 100;
fox_price = ( ( 0.2 * randFloat() + 0.80 ) * 100 + 0.5 ) / 100;
print( "SUPPLIES AT FORT HOCHELAGA COST $150.00." );
print( "YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00." );
player_funds -= 160;
}
else if ( which_fort == FORT_QUEBEC )
{
mink_price = ( ( 0.30 * randFloat() + 0.85 ) * 100 + 0.5 ) / 100;
ermine_price = ( ( 0.15 * randFloat() + 0.80 ) * 100 + 0.5 ) / 100;
beaver_price = ( ( 0.20 * randFloat() + 0.90 ) * 100 + 0.5 ) / 100;
fox_price = ( ( 0.25 * randFloat() + 1.10 ) * 100 + 0.5 ) / 100;
event_picker = ( 10 * randFloat() ) + 1;
if ( event_picker <= 2 )
{
print( "YOUR BEAVER WERE TOO HEAVY TO CARRY ACROSS" );
print( "THE PORTAGE. YOU HAD TO LEAVE THE PELTS, BUT FOUND" );
print( "THEM STOLEN WHEN YOU RETURNED." );
player_furs[ FUR_BEAVER ] = 0;
}
else if ( event_picker <= 6 )
{
print( "YOU ARRIVED SAFELY AT FORT STADACONA." );
}
else if ( event_picker <= 8 )
{
print( "YOUR CANOE UPSET IN THE LACHINE RAPIDS. YOU" );
print( "LOST ALL YOUR FURS." );
zeroInventory( player_furs );
}
else if ( event_picker <= 10 )
{
print( "YOUR FOX PELTS WERE NOT CURED PROPERLY." );
print( "NO ONE WILL BUY THEM." );
player_furs[ FUR_FOX ] = 0;
}
else
{
printf( "Internal Error #3, Out-of-bounds event_picker %d\n", event_picker );
exit( 1 ); /* you have a bug */
}
print( "" );
print( "SUPPLIES AT FORT STADACONA COST $125.00." );
print( "YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00." );
player_funds -= 140;
}
else if ( which_fort == FORT_NEWYORK )
{
mink_price = ( ( 0.15 * randFloat() + 1.05 ) * 100 + 0.5 ) / 100;
ermine_price = ( ( 0.15 * randFloat() + 0.95 ) * 100 + 0.5 ) / 100;
beaver_price = ( ( 0.25 * randFloat() + 1.00 ) * 100 + 0.5 ) / 100;
if ( fox_price < 0 )
{
/* Original Bug? There is no Fox price generated for New York,
it will use any previous "D1" price.
So if there was no previous value, make one up */
fox_price = ( ( 0.25 * randFloat() + 1.05 ) * 100 + 0.5 ) / 100; /* not in orginal code */
}
event_picker = ( 10 * randFloat() ) + 1;
if ( event_picker <= 2 )
{
print( "YOU WERE ATTACKED BY A PARTY OF IROQUOIS." );
print( "ALL PEOPLE IN YOUR TRADING GROUP WERE" );
print( "KILLED. THIS ENDS THE GAME." );
exit( 0 );
}
else if ( event_picker <= 6 )
{
print( "YOU WERE LUCKY. YOU ARRIVED SAFELY" );
print( "AT FORT NEW YORK." );
}
else if ( event_picker <= 8 )
{
print( "YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY." );
print( "HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND." );
zeroInventory( player_furs );
}
else if ( event_picker <= 10 )
{
mink_price /= 2;
fox_price /= 2;
print( "YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP." );
print( "YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS." );
}
else
{
print( "Internal Error #4, Out-of-bounds event_picker %d\n" );
exit( 1 ); /* you have a bug */
}
print( "" );
print( "SUPPLIES AT NEW YORK COST $85.00." );
print( "YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00." );
player_funds -= 105;
}
else
{
printf( "Internal error #2, fort %d does not exist\n", which_fort );
exit( 1 ); /* you have a bug */
}
/* Calculate sales */
beaver_value = beaver_price * player_furs[ FUR_BEAVER ];
fox_value = fox_price * player_furs[ FUR_FOX ];
ermine_value = ermine_price * player_furs[ FUR_ERMINE ];
mink_value = mink_price * player_furs[ FUR_MINK ];
print( "" );
printf( "YOUR BEAVER SOLD FOR $%6.2f\n", beaver_value );
printf( "YOUR FOX SOLD FOR $%6.2f\n", fox_value );
printf( "YOUR ERMINE SOLD FOR $%6.2f\n", ermine_value );
printf( "YOUR MINK SOLD FOR $%6.2f\n", mink_value );
player_funds += beaver_value + fox_value + ermine_value + mink_value;
print( "" );
printf( "YOU NOW HAVE $ %1.2f INCLUDING YOUR PREVIOUS SAVINGS\n", player_funds );
print( "" );
print( "DO YOU WANT TO TRADE FURS NEXT YEAR?" );
yes_or_no = getYesOrNo();
if ( yes_or_no == 'N' )
exit( 0 ); /* STOP */
else
game_state = STATE_TRADING;
}
}
return 0; /* exit OK */
}