This commit is contained in:
Mark Yocom
2021-03-20 16:43:48 -07:00
34 changed files with 5057 additions and 130 deletions

View File

@@ -6,10 +6,13 @@ https://www.atariarchives.org/basicgames/showpage.php?page=40
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
The file `checkers.annotated.bas` contains an indented and annotated
version of the source code. This is no longer valid BASIC code but
should be more readable.
## Known Issues In the Original BASIC Code
- If the computer moves a checker to the bottom row, it promotes, but
leaves the original checker in place. (See line 1240)
- Human players may move non-kings as if they were kings. (See lines 1590 to 1810)
- Human players are not required to jump if it is possible.
- Curious writing to "I" variable without ever reading it. (See lines 1700 and 1806)

View File

@@ -0,0 +1,315 @@
# Annotated version of CHECKERS.BAS, modified to improve readability.
#
# I've made the following changes:
#
# 1. Added many comments and blank lines.
# 2. Separated each statement into its own line.
# 3. Indented loops, conditionals and subroutines.
# 4. Turned *SOME* conditionals and loops into
# structured-BASIC-style if/endif and loop/endloop blocks.
# 5. Switched to using '#' to delimit comments.
# 6. Subroutines now begin with "Sub_Start"
# 7. All non-string text has been converted to lower-case
# 8. All line numbers that are not jump destinations have been removed.
#
# This has helped me make sense of the code. I hope it will also help you.
#
# Print the banner
print tab(32);"CHECKERS"
print tab(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
print
print
print
print "THIS IS THE GAME OF CHECKERS. THE COMPUTER IS X,"
print "AND YOU ARE O. THE COMPUTER WILL MOVE FIRST."
print "SQUARES ARE REFERRED TO BY A COORDINATE SYSTEM."
print "(0,0) IS THE LOWER LEFT CORNER"
print "(0,7) IS THE UPPER LEFT CORNER"
print "(7,0) IS THE LOWER RIGHT CORNER"
print "(7,7) IS THE UPPER RIGHT CORNER"
print "THE COMPUTER WILL TYPE '+TO' WHEN YOU HAVE ANOTHER"
print "JUMP. TYPE TWO NEGATIVE NUMBERS IF YOU CANNOT JUMP."
print
print
print
# Declare the "globals":
# The current move: (rating, current x, current y, new x, new y)
# 'rating' represents how good the move is; higher is better.
dim r(4)
r(0)=-99 # Start with minimum score
# The board. Pieces are represented by numeric values:
#
# - 0 = empty square
# - -1,-2 = X (-1 for regular piece, -2 for king)
# - 1,2 = O (1 for regular piece, 2 for king)
#
# This program's player ("me") plays X.
dim s(7,7)
g=-1 # constant holding -1
# Initialize the board. Data is 2 length-wise strips repeated.
data 1,0,1,0,0,0,-1,0,0,1,0,0,0,-1,0,-1,15
for x=0 to 7
for y=0 to 7
read j
if j=15 then 180
s(x,y)=j
goto 200
180 restore
read s(x,y)
200 next y,x
230 # Start of game loop. First, my turn.
# For each square on the board, search for one of my pieces
# and if it can make the best move so far, store that move in 'r'
for x=0 to 7
for y=0 to 7
# Skip if this is empty or an opponent's piece
if s(x,y) > -1 then 350
# If this is one of my ordinary pieces, analyze possible
# forward moves.
if s(x,y) = -1 then
for a=-1 to 1 step 2
b=g
gosub 650
next a
endif
# If this is one of my kings, analyze possible forward
# and backward moves.
if s(x,y) = -2 then
for a=-1 to 1 step 2
for b=-1 to 1 step 2
gosub 650
next b,a
endif
350 next y,x
goto 1140 # Skip the subs
# Analyze a move from (x,y) to (x+a, y+b) and schedule it if it's
# the best candidate so far.
650 Sub_Start
u=x+a
v=y+b
# Done if it's off the board
if u<0 or u>7 or v<0 or v>7 then 870
# Consider the destination if it's empty
if s(u,v) = 0 then
gosub 910
goto 870
endif
# If it's got an opponent's piece, jump it instead
if s(u,v) > 0
# Restore u and v, then return if it's off the board
u=u+a
v=v+b
if u<0 or v<0 or u>7 or v>7 then 870
# Otherwise, consider u,v
if s(u,v)=0 then gosub 910
endif
870 return
# Evaluate jumping (x,y) to (u,v).
#
# Computes a score for the proposed move and if it's higher
# than the best-so-far move, uses that instead by storing it
# and its score in array 'r'.
910 Sub_Start
# q is the score; it starts at 0
# +2 if it promotes this piece
if v=0 and s(x,y)=-1 then q=q+2
# +5 if it takes an opponent's piece
if abs(y-v)=2 then q=q+5
# -2 if the piece is moving away from the top boundary
if y=7 then q=q-2
# +1 for putting the piece against a vertical boundary
if u=0 or u=7 then q=q+1
for c=-1 to 1 step 2
if u+c < 0 or u+c > 7 or v+g < 0 then 1080
# +1 for each adjacent friendly piece
if s(u+c, v+g) < 0 then
q=q+1
goto 1080
endif
# Prevent out-of-bounds testing
if u-c < 0 or u-c > 7 or v-g > 7 then 1080
# -2 for each opponent piece that can now take this piece here
if s(u+c,v+g) > 0 and(s(u-c,v-g)=0 or(u-c=x and v-g=y))then q=q-2
1080 next c
# Use this move if it's better than the previous best
if q>r(0) then
r(0)=q
r(1)=x
r(2)=y
r(3)=u
r(4)=v
endif
q=0 # reset the score
return
1140 if r(0)=-99 then 1880 # Game is lost if no move could be found.
# Print the computer's move. (Note: chr$(30) is an ASCII RS
# (record separator) code; probably no longer relevant.)
print chr$(30)"FROM"r(1);r(2)"TO"r(3);r(4);
r(0)=-99
# Make the computer's move. If the piece finds its way to the
# end of the board, crown it.
1240 if r(4)=0 then
s(r(3),r(4))=-2
goto 1420
endif
s(r(3),r(4))=s(r(1),r(2))
s(r(1),r(2))=0
# If the piece has jumped 2 squares, it means the computer has
# taken an opponents' piece.
if abs(r(1)-r(3)) == 2 then
s((r(1)+r(3))/2,(r(2)+r(4))/2)=0 # Delete the opponent's piece
# See if we can jump again. Evaluate all possible moves.
x=r(3)
y=r(4)
for a=-2 to 2 step 4
if s(x,y)=-1 then
b=-2
gosub 1370
endif
if s(x,y)=-2 then
for b=-2 to 2 step 4
gosub 1370
next b
endif
next a
# If we've found a move, go back and make that one as well
if r(0) <> -99 then
print "TO" r(3); r(4);
r(0)=-99
goto 1240
endif
goto 1420 # Skip the sub
# If (u,v) is in the bounds, evaluate it as a move using
# the sub at 910
1370 Sub_Start
u=x+a
v=y+b
if u<0 or u>7 or v<0 or v>7 then 1400
if s(u,v)=0 and s(x+a/2,y+b/2)>0 then gosub 910
1400 return
1420 endif
# Now, print the board
print
print
print
for y=7 to 0 step-1
for x=0 to 7
i=5*x
print tab(i);
if s(x,y)=0 then print".";
if s(x,y)=1 then print"O";
if s(x,y)=-1 then print"X";
if s(x,y)=-2 then print"X*";
if s(x,y)=2 then print"O*";
next x
print" "
print
next y
print
# Check if either player is out of pieces. If so, announce the
# winner.
for l=0 to 7
for m=0 to 7
if s(l,m)=1 or s(l,m)=2 then z=1
if s(l,m)=-1 or s(l,m)=-2 then t=1
next m
next l
if z<>1 then 1885
if t<>1 then 1880
# Prompt the player for their move.
z=0
t=0
1590 input "FROM";e,h
x=e
y=h
if s(x,y)<=0 then 1590
1670 input "TO";a,b
x=a
y=b
if s(x,y)=0 and abs(a-e)<=2 and abs(a-e)=abs(b-h)then 1700
print chr$(7)chr$(11); # bell, vertical tab; invalid move
goto 1670
1700 i=46 # Not used; probably a bug
1750 loop
# Make the move and stop unless it might be a jump.
s(a,b) = s(e,h)
s(e,h) = 0
if abs(e-a) <> 2 then break
# Remove the piece jumped over
s((e+a)/2,(h+b)/2) = 0
# Prompt for another move; -1 means player can't, so I've won.
# Keep prompting until there's a valid move or the player gives
# up.
1802 input "+TO";a1,b1
if a1 < 0 then break
if s(a1,b1) <> 0 or abs(a1-a) <>2 or abs(b1-b) <> 2 then 1802
# Update the move variables to correspond to the next jump
e=a
h=b
a=a1
b=b1
i=i+15 # Not used; probably a bug
endloop
# If the player has reached the end of the board, crown this piece
1810 if b=7 then s(a,b)=2
# And play the next turn.
goto 230
# Endgame:
1880 print
print "YOU WIN."
end
1885 print
print "I WIN."
end

View File

@@ -1,3 +1,8 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Ruby](https://www.ruby-lang.org/en/)
This version preserves the underlying algorithms and functionality of
the original while using more modern programming constructs
(functions, classes, symbols) and providing much more detailed
comments. It also fixes some (but not all) of the bugs.

View File

@@ -0,0 +1,651 @@
#!/usr/bin/env ruby
# Checkers in Ruby, Version 1
#
# This version of the game attempts to preserve the underlying
# algorithm(s) and feel of the BASIC version while using more modern
# coding techniques. Specifically:
#
# 1. The major data structures (the board and current move, known as S
# and R in the BASIC version) have been turned into classes. In
# addition, I made a class for coordinates so that you don't always
# have to deal with pairs of numbers.
#
# 2. Much of the functionality associated with this data has been moved
# into methods of these classes in line with the philosophy that objects
# are smart data.
#
# 3. While I've kept the board as a single object (not global, though),
# this program will create many Move objects (i.e. copies of the move
# under consideration) rather than operating on a single global
# instance.
#
# 4. The rest of the code has been extracted into Ruby functions with
# all variables as local as reasonably possible.
#
# 5. Pieces are now represented with Symbols instead of integers; this
# made it *much* easier to understand what was going on.
#
# 6. There are various internal sanity checks. They fail by throwing
# a string as an exception. (This is generally frowned upon if
# you're going to catch the exception later but we never do that;
# an exception here means a bug in the software and the way to fix
# that is to fix the program.)
#
# And probably other stuff.
#
# Note: I've ordered the various major definitions here from (roughly)
# general to specific so that if you read the code starting from the
# beginning, you'll (hopefully) get a big-picture view first and then
# get into details. Normally, I'd order things by topic and define
# things before using them, which is a better ordering. So in this
# case, do what I say and not what I do.
#
# Some global constants
#
BOARD_TEXT_INDENT = 30 # Number of spaces to indent the board when printing
# Various constants related to the game of Checkers.
#
# (Yes, they're obvious but if you see BOARD_WIDTH, you know this is
# related to board dimensions in a way that you wouldn't if you saw
# '8'.)
BOARD_WIDTH = 8
KING_ROW_X = 0
KING_ROW_Y = BOARD_WIDTH - 1
# This is the mainline routine of the program. Ruby doesn't require
# that you put this in a function but this way, your local variables
# are contained here. It's also neater, IMO.
#
# The name 'main' isn't special; it's just a function. The last line
# of this program is a call to it.
def main
print <<EOF
Checkers
Inspiration: Creative Computing Morristown, New Jersey
This is the game of checkers. The computer is X,
and you are O. The computer will move first.
Squares are referred to by a coordinate system.
(0,0) is the lower left corner
(0,7) is the upper left corner
(7,0) is the lower right corner
(7,7) is the upper right corner
The computer will tell you when you have another
jump.
Enter a blank line if you cannot make a move. If this
not after a jump, it means you have lost the game.
EOF
board = Board.new
winner = game_loop(board)
puts board.text
puts
puts "#{winner ? 'I' : 'You'} win."
end
# This is the main game loop. Returns true if I win and false if the
# player wins. Each of my_turn and players_turn return false if they
# could not move. (Recall that the game ends when one player can't
# move.)
def game_loop(board)
while true
my_turn(board) or return false
players_turn(board) or return true
end
end
#
# My (i.e. computer's) turn
#
# Play my turn, returning true if it could make at least one move and
# false otherwise. This is pretty simple because the heavy lifting is
# done by the methods of the Board and Move classes.
def my_turn(board, jumpStart = nil)
# Print the initial board
puts board.text
# Find the candidate move. If this is a move after a jump, the
# starting point will be in jumpStart and we use only that.
# Otherwise, we check all available pieces.
bestMove = Move.invalid
candidates = jumpStart ? [jumpStart] : board.my_pieces
for coord in candidates
bestMove = bestMove.betterOf( board.bestMoveFrom(coord, !!jumpStart) )
end
# If we can't find a move, we're done
if !bestMove.valid?
puts "I can't make another #{jumpStart ? 'jump' : 'move'}."
return false
end
# Do the move
puts "My move: #{bestMove}"
canMoveAgain = board.make_move!(bestMove)
# Repeat (recursively) if we can make another jump
my_turn(board, bestMove.to) if canMoveAgain
# No loss yet!
return true
end
#
# Player's turn
#
# Prompt the player for a move and then play it. If the player can
# make multiple moves (i.e. jump), repeat. Return true if at least one
# move was played, false otherwise.
#
# Note that this does not enforce all of the rules of the game; it
# assumes the player knows the rules and will stick to them. In
# particular, the player is not required to jump if possible. This
# behaviour was inherited from the BASIC version but I've made some
# improvements so we will catch most illegal moves; in that case, the
# player is prompted for another move.
def players_turn(board)
from = nil
while true
move = get_player_move(board, from)
# If the player declines to enter a move, we're done. If this is
# the first move ('from' will be nil in this case), it's a forfeit
# and we return false to indicate the player has lost. But if
# it's a jump, the player was able to move and therefor has not
# lost so we return true. (Note: this requires the player to not
# cheat, which is kind of a bug but that's how the original worked
# as well.)
return false if !from && !move
return true if from && !move
canMoveAgain = board.make_move!(move)
return true unless canMoveAgain
# If the player can jump again, repeat from the new position.
from = move.to
end
# Can't reach here
end
# Prompt the player for a move, read it, check if it's legal and
# return it if it is or try again if it isn't. If the player declines
# to move, returns nil. If this is a jump following a previous jump,
# the second argument will be the source (i.e. Move.from) and we don't
# ask for it here..
def get_player_move(board, jumpFrom = nil)
puts "You can jump again!" if jumpFrom
while true
puts board.text
puts "Enter your move:"
puts "From? #{jumpFrom}" if jumpFrom
from = jumpFrom || read_coord("From? ")
to = read_coord("To? ") unless !from
# If the player entered a blank line, it's a forfeit
if !from || !to
return nil if jumpFrom || confirm_quit()
end
move = Move.new(from, to)
return move if board.legal_move?(false, move)
print "\nThis move is not legal!\n\n"
end
end
# Prompt the player for the x,y coordinates of a piece on the board,
# repeating until a valid coordinate is provided and return it. If
# the player enters a blank line, returns nil; this is interpreted as
# declining to move.
def read_coord(prompt)
while true
print prompt
STDOUT.flush
line = (STDIN.gets || "").strip
return nil if line == ''
coord = parse_coord(line)
return coord if coord
puts "Invalid input; try again!"
end
end
# Ask the player if they wish to quit; return true if they do, false
# otherwise. Tends to assume false unless given an explicit yes.
def confirm_quit
print "Really forfeit (y/n) "
STDOUT.flush
line = STDIN.gets.strip
return !!(line =~ /^y(es)?$/i) # For extra credit, explain why I use '!!'
end
# Parse a string containing x,y coordinates to produce and return a
# new Coord object. Returns nil if the string is not a valid
# coordinate.
def parse_coord(coord)
coord = coord.gsub(/\s/, '')
return nil unless coord =~ /^\d,\d$/
parts = coord.split(/,/)
result = Coord.new(parts[0].to_i, parts[1].to_i)
return nil unless result.valid?
return result
end
#
# Classes
#
# Class to represent the game board and all of the pieces on it.
#
# Like the BASIC version, the board is represented using an 8 by 8
# array. However, instead of using numbers between -2 and 2, we
# represent board squares with Ruby symbols:
#
# - :_ (underscore) is an empty square
# - :o and :x (lowercase) are ordinary pieces for their respective sides
# - :O and :X (uppercase) are kings for their respective sides
#
# Most of the smarts of the game are also here as methods of this
# class. So my_turn() doesn't (e.g.) go through the board squares
# looking for the best move; it asks the board for it by calling
# bestMove().
#
class Board
# Set up the board to the starting position
def initialize
@board = []
col1 = %i{o _ o _ _ _ x _}
col2 = %i{_ o _ _ _ x _ x}
4.times {
@board += [col1.dup, col2.dup]
}
end
# We use [] and []= (the array/hash get and set operators) to get
# individual board pieces instead of accessing it directly; this
# prevents errors where you get the row and column wrong in some
# places and not others. They're private because it turns out that
# nothing outside of this class needs them but we could make them
# public without a lot of trouble. The position must be a Coord
# object.
private
def [](coord)
return nil unless coord.valid?
return @board[coord.x][coord.y]
end
def []=(coord, value)
# Sanity checks
raise "Invalid coordinate: #{coord}" unless coord.valid?
raise "Invalid board value: #{value}" unless %i{_ x o X O}.include?(value)
@board[coord.x][coord.y] = value
end
public
# Return an ASCII picture of the board. This is what gets printed
# between turns.
def text(indent = BOARD_TEXT_INDENT)
result = ""
glyph = {_: '.'} # Replace some symbols when printing for readability
for y in (0 .. 7).to_a.reverse
result += ' '*indent + y.to_s + ' '
for x in (0 .. 7)
result += glyph.fetch(self[ Coord.new(x, y) ]) {|k| k.to_s}
end
result += "\n"
end
result += ' '*indent + ' ' + (0..7).map{|n| n.to_s}.join
return result + "\n\n"
end
# Answer various questions about positions on the board
def mine_at?(coord)
return %i{x X}.include? self[coord]
end
def king_at?(coord)
return %i{X O}.include? self[coord]
end
def opponent_at?(coord)
return %i{o O}.include? self[coord]
end
def empty_at?(coord)
return self[coord] == :_
end
# Return a list of all valid positions on the board
def all_coords
coords = []
for x in 0 .. BOARD_WIDTH - 1
for y in 0 .. BOARD_WIDTH - 1
coords.push Coord.new(x, y)
end
end
return coords
end
# Return a list of all coords containing my (i.e. X's) pieces.
def my_pieces
all_coords.select{|coord| mine_at?(coord)}
end
private
# Return a list of all valid positions on the board. (To do: skip
# all light-coloured squares, since they will never hold a piece.)
def all_coords
coords = []
for x in 0 .. BOARD_WIDTH - 1
for y in 0 .. BOARD_WIDTH - 1
coords.push Coord.new(x, y)
end
end
return coords
end
public
# Test if the move 'move' is legal; tests for me (i.e. the computer)
# if 'is_me' is true and for the human player if 'is_me' is false.
def legal_move?(is_me, move)
# If the move isn't valid, it's not legal.
return false unless move.valid?
# There's a piece belonging to me at the source
return false unless mine_at?(move.from) == is_me
# The destination is empty
return false unless empty_at?(move.to)
# The source holds one of the players' pieces
return false if empty_at?(move.from) || mine_at?(move.from) != is_me
# moving forward if not a king
if !king_at?(move.from)
return false if is_me && move.to.y > move.from.y
return false if !is_me && move.to.y < move.from.y
end
# If jumping, that there's an opponent's piece between the start
# and end.
return false if
move.jump? &&
(empty_at?(move.midpoint) || opponent_at?(move.midpoint) != is_me)
# Otherwise, it's legal
return true
end
# Perform 'move' on the board. 'move' must be legal; the player
# performing it is determined by the move's starting ('from')
# position.
def make_move!(move)
piece = self[move.from]
# Sanity check
raise "Illegal move: #{move}" unless legal_move?(piece.downcase == :x,move)
# Promote the piece if it's reached the end row
piece = piece.upcase if
(piece == :x && move.to.y == KING_ROW_X) ||
(piece == :o && move.to.y == KING_ROW_Y)
# And do the move
self[move.to] = piece
self[move.from] = :_
# Remove the piece jumped over if this is a jump
self[move.midpoint] = :_ if move.jump?
end
# Return the best (i.e. likely to win) move possible for the
# piece (mine) at 'coord'.
def bestMoveFrom(coord, mustJump)
so_far = Move.invalid
return so_far unless coord.valid?
offsets = [ [-1, -1], [1, -1]]
offsets += [ [-1, 1], [1, 1]] if king_at?(coord)
for ofx, ofy in offsets
new_coord = coord.by(ofx, ofy)
if opponent_at?(new_coord)
new_coord = new_coord.by(ofx, ofy)
elsif mustJump
next
end
next unless new_coord.valid?
so_far = so_far.betterOf( ratedMove(coord, new_coord) )
end
return so_far
end
# Create and return a move for *me* from Coords 'from' to 'to' with
# its 'rating' set to how good the move looks according to criteria
# used by the BASIC version of this program. If the move is
# illegal, returns an invalid Move object.
def ratedMove(from, to)
return Move.invalid unless legal_move?(true, Move.new(from, to))
rating = 0
# +2 if it promotes this piece
rating += 2 if to.y == 0
# +50 if it takes the opponent's piece. (Captures are mandatory
# so we ensure that a capture will always outrank a non-capture.)
rating += 4 if (from.y - to.y).abs == 2
# -2 if we're moving away from the king row
rating -= 2 if from.y == BOARD_WIDTH - 1
# +1 for putting the piece against a vertical boundary
rating += 1 if to.x == 0 || to.x == BOARD_WIDTH - 1
# +1 for each friendly piece behind this one
[-1, 1].each {|c|
rating += 1 if mine_at?( to.by(c, -1) )
}
# -2 for each opponent's piece that can now capture this one.
# (This includes a piece that may be captured when moving here;
# this is a bug.)
[ -1, 1].each {|c|
there = to.by(c, -1)
opposite = to.by(-c, 1)
rating -= 2 if
opponent_at?(there) && (empty_at?(opposite) || opposite == from)
}
return Move.new(from, to, rating)
end
end
# Class to hold the X and Y coordinates of a position on the board.
#
# Coord objects are immutable--that is, they never change after
# creation. Instead, you will always get a modified copy back.
class Coord
# Coordinates are readable
attr_reader :x, :y
# Initialize
def initialize(x, y)
@x, @y = [x,y]
end
# Test if this move is on the board.
def valid?
return x >= 0 && y >= 0 && x < BOARD_WIDTH && y < BOARD_WIDTH
end
# Test if this Coord is equal to another Coord.
def ==(other)
return other.class == self.class && other.x == @x && other.y == y
end
# Return a string that describes this Coord in a human-friendly way.
def to_s
return "(#{@x},#{@y})"
end
# Return a new Coord whose x and y coordinates have been adjusted by
# arguments 'x' and 'y'.
def by(x, y)
return Coord.new(@x + x, @y + y)
end
end
# Class to represent a move by a player between two positions,
# possibly with a rating that can be used to select the best of a
# collection of moves.
#
# An (intentionally) invalid move will have a value of nil for both
# 'from' and 'to'. Most methods other than 'valid?' assume the Move
# is valid.
class Move
# Readable fields:
attr_reader :from, :to, :rating
# The initializer; -99 is the lowest rating from the BASIC version
# so we use that here as well.
def initialize(from, to, rating = -99)
@from, @to, @rating = [from, to, rating]
# Sanity check; the only invalid Move tolerated is the official
# one (i.e. with nil for each endpoint.)
raise "Malformed Move: #{self}" if @from && @to && !valid?
end
# Return an invalid Move object.
def self.invalid
return self.new(nil, nil, -99)
end
# Return true if this is a valid move (i.e. as close to legal as we
# can determine without seeing the board.)
def valid?
# Not valid if @from or @to is nil
return false unless @from && @to
# Not valid unless both endpoints are on the board
return false unless @from.valid? && @to.valid?
# Not valid unless it's a diagonal move by 1 or 2 squares
dx, dy = delta
return false if dx.abs != dy.abs || (dx.abs != 1 && dx.abs != 2)
# Otherwise, valid
return true
end
# Return true if this move is a jump, false otherwise
def jump?
return valid? && magnitude() == 2
end
# Return the coordinates of the piece being jumped over by this
# move.
def midpoint
raise "Called 'midpoint' on a non-jump move!" unless jump?
dx, dy = delta
return @from.by(dx / dx.abs, dy / dy.abs)
end
# Return the better-rated of self or otherMove.
def betterOf(otherMove)
return otherMove if !valid?
return rating > otherMove.rating ? self : otherMove
end
# Return a human-friendly string representing this move.
def to_s
return "[NOMOVE]" if !@from && !@to # Well-known invalid move
jumpover = jump? ?
"-> #{midpoint} ->"
: "->"
return "#{@from} #{jumpover} #{to}#{valid? ? '' : ' [INVALID]'}"
end
private
# Return the distance (x, y) between the 'from' and 'to' locations.
def delta
return [to.x - from.x, to.y - from.y]
end
# Return the number of squares this move will take the piece (either
# 1 or 2).
def magnitude
# Note: we assume that this move is a legal move (and therefore
# diagonal); otherwise, this may not be correct.
return (to.x - from.x).abs
end
end
# Start the game
main()

View File

@@ -221,7 +221,7 @@
2330 PRINT "CONFEDERATE:"INT(100*(C5/C1)+.5)"% OF THE ORIGINAL"
2340 PRINT "UNION: "INT(100*(C6/C2)+.5)"% OF THE ORIGINAL"
2350 PRINT
2360 REM - 1 WHO ONE
2360 REM - 1 WHO WON
2370 IF U <> 1 THEN 2380
2375 IF U2=1 THEN 2460
2380 IF U=1 THEN 2420

View File

@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31005.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CivilWar", "CivilWar\CivilWar.csproj", "{09C22BBE-8480-4B8C-9A07-E2DAA24B692B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{09C22BBE-8480-4B8C-9A07-E2DAA24B692B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09C22BBE-8480-4B8C-9A07-E2DAA24B692B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09C22BBE-8480-4B8C-9A07-E2DAA24B692B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09C22BBE-8480-4B8C-9A07-E2DAA24B692B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {59BCA2DE-D5C7-40F7-BB99-B5C8D2416D8B}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace CivilWar
{
public class Army
{
private enum Resource
{
Food,
Salaries,
Ammunition
}
public Army(Side side)
{
Side = side;
}
public Side Side { get; }
// Cumulative
public int Wins { get; private set; } // W, L
public int Losses { get; private set; } // L, W
public int Draws { get; private set; } // W0
public int BattlesFought => Wins + Draws + Losses;
public bool Surrendered { get; private set; } // Y, Y2 == 5
public int CumulativeHistoricCasualties { get; private set; } // P1, P2
public int CumulativeSimulatedCasualties { get; private set; } // T1, T2
public int CumulativeHistoricMen { get; private set; } // M3, M4
private int income; // R1, R2
private int moneySpent; // Q1, Q2
private bool IsFirstBattle => income == 0;
// This battle
private int historicMen; // M1, M2
public int HistoricCasualties { get; private set; }
public int Money { get; private set; } // D(n)
public int Men { get; private set; } // M5, M6
public int Inflation { get; private set; } // I1, I2
public int InflationDisplay => Side == Side.Confederate ? Inflation + 15 : Inflation; // Confederate inflation is shown with 15 added - no idea why!
private readonly Dictionary<Resource, int> allocations = new(); // F(n), H(n), B(n) for food, salaries, ammunition
public int Strategy { get; protected set; } // Y1, Y2
public double Morale => (2.0 * allocations[Resource.Food] * allocations[Resource.Food] + allocations[Resource.Salaries] * allocations[Resource.Salaries]) / (reducedAvailableMen * reducedAvailableMen + 1); // O, O2
public int Casualties { get; protected set; } // C5, C6
public int Desertions { get; protected set; } // E, E2
public int MenLost => Casualties + Desertions;
public bool AllLost { get; private set; } // U, U2
private double reducedAvailableMen; // F1
protected virtual double FractionUnspent => (income - moneySpent) / (income + 1.0);
public void PrepareBattle(int men, int casualties)
{
historicMen = men;
HistoricCasualties = casualties;
Inflation = 10 + (Losses - Wins) * 2;
Money = 100 * (int)(men * (100 - Inflation) / 2000.0 * (1 + FractionUnspent) + 0.5);
Men = (int)(men * 1 + (CumulativeHistoricCasualties - CumulativeSimulatedCasualties) / (CumulativeHistoricMen + 1.0));
reducedAvailableMen = men * 5.0 / 6.0;
}
public virtual void AllocateResources()
{
Console.WriteLine($"{Side} General ---\nHow much do you wish to spend for");
while (true)
{
foreach (Resource resource in Enum.GetValues<Resource>())
{
if (EnterResource(resource))
break;
}
if (allocations.Values.Sum() <= Money)
return;
Console.WriteLine($"Think again! You have only ${Money}");
}
}
private bool EnterResource(Resource resource)
{
while (true)
{
Console.WriteLine($" - {resource}");
switch ((int.TryParse(Console.ReadLine(), out int val), val))
{
case (false, _):
Console.WriteLine("Not a valid number");
break;
case (_, < 0):
Console.WriteLine("Negative values not allowed");
break;
case (_, 0) when IsFirstBattle:
Console.WriteLine("No previous entries");
break;
case (_, 0):
Console.WriteLine("Assume you want to keep same allocations");
return true;
case (_, > 0):
allocations[resource] = val;
return false;
}
}
}
public virtual void DisplayMorale()
{
Console.WriteLine($"{Side} morale is {Morale switch { < 5 => "Poor", < 10 => "Fair", _ => "High" }}");
}
public virtual bool ChooseStrategy(bool isReplay) => EnterStrategy(true, "(1-5)");
protected bool EnterStrategy(bool canSurrender, string hint)
{
Console.WriteLine($"{Side} strategy {hint}");
while (true)
{
switch ((int.TryParse(Console.ReadLine(), out int val), val))
{
case (false, _):
Console.WriteLine("Not a valid number");
break;
case (_, 5) when canSurrender:
Surrendered = true;
Console.WriteLine($"The {Side} general has surrendered");
return true;
case (_, < 1 or >= 5):
Console.WriteLine($"Strategy {val} not allowed.");
break;
default:
Strategy = val;
return false;
}
}
}
public virtual void CalculateLosses(Army opponent)
{
AllLost = false;
int stratFactor = 2 * (Math.Abs(Strategy - opponent.Strategy) + 1);
Casualties = (int)Math.Round(HistoricCasualties * 0.4 * (1 + 1.0 / stratFactor) * (1 + 1 / Morale) * (1.28 + reducedAvailableMen / (allocations[Resource.Ammunition] + 1)));
Desertions = (int)Math.Round(100 / Morale);
// If losses > men present, rescale losses
if (MenLost > Men)
{
Casualties = 13 * Men / 20;
Desertions = Men - Casualties;
AllLost = true;
}
}
public void RecordResult(Side winner)
{
if (winner == Side)
Wins++;
else if (winner == Side.Both)
Draws++;
else
Losses++;
CumulativeSimulatedCasualties += MenLost;
CumulativeHistoricCasualties += HistoricCasualties;
moneySpent += allocations.Values.Sum();
income += historicMen * (100 - Inflation) / 20;
CumulativeHistoricMen += historicMen;
LearnStrategy();
}
protected virtual void LearnStrategy() { }
public void DisplayWarResult(Army opponent)
{
Console.WriteLine("\n\n\n\n");
Console.WriteLine($"The {Side} general has won {Wins} battles and lost {Losses}");
Side winner = (Surrendered, opponent.Surrendered, Wins < Losses) switch
{
(_, true, _) => Side,
(true, _, _) or (_, _, true) => opponent.Side,
_ => Side
};
Console.WriteLine($"The {winner} general has won the war\n");
}
public virtual void DisplayStrategies() { }
}
class ComputerArmy : Army
{
public int[] StrategyProb { get; } = { 25, 25, 25, 25 }; // S(n)
private readonly Random strategyRng = new();
public ComputerArmy(Side side) : base(side) { }
protected override double FractionUnspent => 0.0;
public override void AllocateResources() { }
public override void DisplayMorale() { }
public override bool ChooseStrategy(bool isReplay)
{
if (isReplay)
return EnterStrategy(false, $"(1-4; usually previous {Side} strategy)");
// Basic code comments say "If actual strategy info is in data then r-100 is extra weight given to that strategy" but there's no data or code to do it.
int strategyChosenProb = strategyRng.Next(100); // 0-99
int sumProbs = 0;
for (int i = 0; i < 4; i++)
{
sumProbs += StrategyProb[i];
if (strategyChosenProb < sumProbs)
{
Strategy = i + 1;
break;
}
}
Console.WriteLine($"{Side} strategy is {Strategy}");
return false;
}
protected override void LearnStrategy()
{
// Learn present strategy, start forgetting old ones
// - present strategy gains 3 * s, others lose s probability points, unless a strategy falls below 5 %.
const int s = 3;
int presentGain = 0;
for (int i = 0; i < 4; i++)
{
if (StrategyProb[i] >= 5)
{
StrategyProb[i] -= s;
presentGain += s;
}
}
StrategyProb[Strategy - 1] += presentGain;
}
public override void CalculateLosses(Army opponent)
{
Casualties = (int)(17.0 * HistoricCasualties * opponent.HistoricCasualties / (opponent.Casualties * 20));
Desertions = (int)(5 * opponent.Morale);
}
public override void DisplayStrategies()
{
ConsoleUtils.WriteWordWrap($"\nIntelligence suggests that the {Side} general used strategies 1, 2, 3, 4 in the following percentages:");
Console.WriteLine(string.Join(", ", StrategyProb));
}
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
namespace CivilWar
{
public enum Side { Confederate, Union, Both }
public enum Option { Battle, Replay, Quit }
public record Battle(string Name, int[] Men, int[] Casualties, Side Offensive, string Description)
{
public static readonly List<Battle> Historic = new()
{
new("Bull Run", new[] { 18000, 18500 }, new[] { 1967, 2708 }, Side.Union, "July 21, 1861. Gen. Beauregard, commanding the south, met Union forces with Gen. McDowell in a premature battle at Bull Run. Gen. Jackson helped push back the union attack."),
new("Shiloh", new[] { 40000, 44894 }, new[] { 10699, 13047 }, Side.Both, "April 6-7, 1862. The confederate surprise attack at Shiloh failed due to poor organization."),
new("Seven Days", new[] { 95000, 115000 }, new[] { 20614, 15849 }, Side.Both, "June 25-july 1, 1862. General Lee (csa) upheld the offensive throughout the battle and forced Gen. McClellan and the union forces away from Richmond."),
new("Second Bull Run", new[] { 54000, 63000 }, new[] { 10000, 14000 }, Side.Confederate, "Aug 29-30, 1862. The combined confederate forces under Lee and Jackson drove the union forces back into Washington."),
new("Antietam", new[] { 40000, 50000 }, new[] { 10000, 12000 }, Side.Both, "Sept 17, 1862. The south failed to incorporate Maryland into the confederacy."),
new("Fredericksburg", new[] { 75000, 120000 }, new[] { 5377, 12653 }, Side.Union, "Dec 13, 1862. The confederacy under Lee successfully repulsed an attack by the union under Gen. Burnside."),
new("Murfreesboro", new[] { 38000, 45000 }, new[] { 11000, 12000 }, Side.Union, "Dec 31, 1862. The south under Gen. Bragg won a close battle."),
new("Chancellorsville", new[] { 32000, 90000 }, new[] { 13000, 17197 }, Side.Confederate, "May 1-6, 1863. The south had a costly victory and lost one of their outstanding generals, 'stonewall' Jackson."),
new("Vicksburg", new[] { 50000, 70000 }, new[] { 12000, 19000 }, Side.Union, "July 4, 1863. Vicksburg was a costly defeat for the south because it gave the union access to the Mississippi."),
new("Gettysburg", new[] { 72500, 85000 }, new[] { 20000, 23000 }, Side.Both, "July 1-3, 1863. A southern mistake by Gen. Lee at Gettysburg cost them one of the most crucial battles of the war."),
new("Chickamauga", new[] { 66000, 60000 }, new[] { 18000, 16000 }, Side.Confederate, "Sept. 15, 1863. Confusion in a forest near Chickamauga led to a costly southern victory."),
new("Chattanooga", new[] { 37000, 60000 }, new[] { 36700, 5800 }, Side.Confederate, "Nov. 25, 1863. After the south had sieged Gen. Rosencrans army for three months, Gen. Grant broke the siege."),
new("Spotsylvania", new[] { 62000, 110000 }, new[] { 17723, 18000 }, Side.Confederate, "May 5, 1864. Grant's plan to keep Lee isolated began to fail here, and continued at Cold Harbor and Petersburg."),
new("Atlanta", new[] { 65000, 100000 }, new[] { 8500, 3700 }, Side.Union, "August, 1864. Sherman and three veteran armies converged on Atlanta and dealt the death blow to the confederacy."),
};
public static (Option, Battle?) SelectBattle()
{
Console.WriteLine("\n\n\nWhich battle do you wish to simulate?");
return int.Parse(Console.ReadLine() ?? "") switch
{
0 => (Option.Replay, null),
>0 and <15 and int n => (Option.Battle, Historic[n-1]),
_ => (Option.Quit, null)
};
}
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CivilWar
{
static class ConsoleUtils
{
public static bool InputYesOrNo()
{
while (true)
{
var answer = Console.ReadLine();
switch (answer?.ToLower())
{
case "no":
return false;
case "yes":
return true;
default:
Console.WriteLine("(Answer Yes or No)");
break;
}
}
}
public static void WriteWordWrap(string text)
{
var line = new StringBuilder(Console.WindowWidth);
foreach (var paragraph in text.Split(Environment.NewLine))
{
line.Clear();
foreach (var word in paragraph.Split(' '))
{
if (line.Length + word.Length < Console.WindowWidth)
{
if (line.Length > 0)
line.Append(' ');
line.Append(word);
}
else
{
Console.WriteLine(line.ToString());
line.Clear().Append(word);
}
}
Console.WriteLine(line.ToString());
}
}
public static void WriteTable<T>(ICollection<T> items, List<TableRow<T>> rows, bool transpose = false)
{
int cols = items.Count + 1;
var content = rows.Select(r => r.Format(items)).ToList();
if (transpose)
{
content = Enumerable.Range(0, cols).Select(col => content.Select(r => r[col]).ToArray()).ToList();
cols = rows.Count;
}
var colWidths = Enumerable.Range(0, cols).Select(col => content.Max(c => c[col].Length)).ToArray();
foreach (var row in content)
{
for (int col = 0; col < cols; col++)
{
var space = new string(' ', colWidths[col] - row[col].Length);
row[col] = col == 0 ? row[col] + space : space + row[col]; // left-align first col; right-align other cols
}
}
var sb = new StringBuilder();
var horizBars = colWidths.Select(w => new string('═', w)).ToArray();
void OneRow(string[] cells, char before, char between, char after)
{
sb.Append(before);
sb.AppendJoin(between, cells);
sb.Append(after);
sb.AppendLine();
}
OneRow(horizBars, '╔', '╦', '╗');
bool first = true;
foreach (var row in content)
{
if (first)
first = false;
else
OneRow(horizBars, '╠', '╬', '╣');
OneRow(row, '║', '║', '║');
}
OneRow(horizBars, '╚', '╩', '╝');
Console.WriteLine(sb.ToString());
}
public record TableRow<T>(string Name, Func<T, object> Data, string Before = "", string After = "")
{
private string FormatItem(T item) => $" {Before}{Data(item)}{After} ";
public string[] Format(IEnumerable<T> items) => items.Select(FormatItem).Prepend($" {Name} ").ToArray();
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using static CivilWar.ConsoleUtils;
namespace CivilWar
{
public record GameOptions(bool TwoPlayers, bool ShowDescriptions)
{
public static GameOptions Input()
{
Console.WriteLine(
@" Civil War
Creative Computing, Morristown, New Jersey
Do you want instructions?");
const string instructions = @"This is a civil war simulation.
To play type a response when the computer asks.
Remember that all factors are interrelated and that your responses could change history. Facts and figures used are based on the actual occurrence. Most battles tend to result as they did in the civil war, but it all depends on you!!
The object of the game is to win as many battles as possible.
Your choices for defensive strategy are:
(1) artillery attack
(2) fortification against frontal attack
(3) fortification against flanking maneuvers
(4) falling back
Your choices for offensive strategy are:
(1) artillery attack
(2) frontal attack
(3) flanking maneuvers
(4) encirclement
You may surrender by typing a '5' for your strategy.";
if (InputYesOrNo())
WriteWordWrap(instructions);
Console.WriteLine("\n\nAre there two generals present?");
bool twoPlayers = InputYesOrNo();
if (!twoPlayers)
Console.WriteLine("\nYou are the confederacy. Good luck!\n");
WriteWordWrap(
@"Select a battle by typing a number from 1 to 14 on request. Type any other number to end the simulation. But '0' brings back exact previous battle situation allowing you to replay it.
Note: a negative Food$ entry causes the program to use the entries from the previous battle
After requesting a battle, do you wish battle descriptions (answer yes or no)");
bool showDescriptions = InputYesOrNo();
return new GameOptions(twoPlayers, showDescriptions);
}
}
}

View File

@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CivilWar;
var options = GameOptions.Input();
var armies = new List<Army> { new Army(Side.Confederate), options.TwoPlayers ? new Army(Side.Union) : new ComputerArmy(Side.Union) };
Battle? battle = null;
while (OneBattle(ref battle)) { }
DisplayResult();
bool OneBattle(ref Battle? previous)
{
var (option, selected) = Battle.SelectBattle();
var (battle, isReplay, quit) = option switch
{
Option.Battle => (selected!, false, false),
Option.Replay when previous != null => (previous, true, false), // can't replay if no previous battle
_ => (null!, false, true),
};
if (quit)
return false;
if (!isReplay)
{
Console.WriteLine($"This is the battle of {battle.Name}.");
if (options.ShowDescriptions)
ConsoleUtils.WriteWordWrap(battle.Description);
armies.ForEach(a => a.PrepareBattle(battle.Men[(int)a.Side], battle.Casualties[(int)a.Side]));
}
ConsoleUtils.WriteTable(armies, new()
{
new("", a => a.Side),
new("Men", a => a.Men),
new("Money", a => a.Money, Before: "$"),
new("Inflation", a => a.InflationDisplay, After: "%")
});
armies.ForEach(a => a.AllocateResources());
armies.ForEach(a => a.DisplayMorale());
string offensive = battle.Offensive switch
{
Side.Confederate => "You are on the offensive",
Side.Union => "You are on the defensive",
_ => "Both sides are on the offensive"
};
Console.WriteLine($"Confederate general---{offensive}");
if (armies.Any(a => a.ChooseStrategy(isReplay)))
{
return false; // someone surrendered
}
armies[0].CalculateLosses(armies[1]);
armies[1].CalculateLosses(armies[0]);
ConsoleUtils.WriteTable(armies, new()
{
new("", a => a.Side),
new("Casualties", a => a.Casualties),
new("Desertions", a => a.Desertions),
});
if (options.TwoPlayers)
{
var oneDataCol = new[] { 1 };
Console.WriteLine($"Compared to the actual casualties at {battle.Name}");
ConsoleUtils.WriteTable(oneDataCol, armies.Select(a => new ConsoleUtils.TableRow<int>(
a.Side.ToString(),
_ => $"{(double)a.Casualties / battle.Casualties[(int)a.Side]}", After: "% of the original")
).ToList());
}
Side winner;
switch (armies[0].AllLost, armies[1].AllLost, armies[0].MenLost - armies[1].MenLost)
{
case (true, true, _) or (false, false, 0):
Console.WriteLine("Battle outcome unresolved");
winner = Side.Both; // Draw
break;
case (false, true, _) or (false, false, < 0):
Console.WriteLine($"The Confederacy wins {battle.Name}");
winner = Side.Confederate;
break;
case (true, false, _) or (false, false, > 0):
Console.WriteLine($"The Union wins {battle.Name}");
winner = Side.Union;
break;
}
if (!isReplay)
{
armies.ForEach(a => a.RecordResult(winner));
}
Console.WriteLine("---------------");
previous = battle;
return true;
}
void DisplayResult()
{
armies[0].DisplayWarResult(armies[1]);
int battles = armies[0].BattlesFought;
if (battles > 0)
{
Console.WriteLine($"For the {battles} battles fought (excluding reruns)");
ConsoleUtils.WriteTable(armies, new()
{
new("", a => a.Side),
new("Historical Losses", a => a.CumulativeHistoricCasualties),
new("Simulated Losses", a => a.CumulativeSimulatedCasualties),
new(" % of original", a => ((double)a.CumulativeSimulatedCasualties / a.CumulativeHistoricCasualties).ToString("p2"))
}, transpose: true);
armies[1].DisplayStrategies();
}
}

View File

@@ -0,0 +1,37 @@
#!/usr/bin/perl
use strict;
################
# PORTING NOTES:
# * In basic "Tab" function are not spaces, but absolute col position on screen.
# * It was too dificult to port this one, couldn't figure out the original algorithm.
# * So the algorithm was remake.
#
print ' 'x 33 . "DIAMOND\n";
print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n"; print "\n"; print "\n";
print "FOR A PRETTY DIAMOND PATTERN,\n";
print "TYPE IN AN ODD NUMBER BETWEEN 5 AND 21? "; chomp(my $R = <STDIN>); print "\n";
my $Wid= int(60/$R)+1;
my $Dia="CC". "!" x ($R-2);
for (my $J=1; $J<$Wid; $J++) {
for (my $K=1; $K<($R+2)*2-4; $K+=2) {
my $Size= $K;
if ($K>$R) { $Size=$R+($R-$K); }
my $Chunk= substr($Dia, 0, $Size);
for (my $L=1; $L<$Wid; $L++) {
my $Space= " " x (($R-$Size)/2);
if ($L>1) { $Space.=$Space; }
print $Space.$Chunk;
}
print "\n";
}
}
exit;

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>KING</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<script src="king.js"></script>
</body>
</html>

376
53 King/javascript/king.js Normal file
View File

@@ -0,0 +1,376 @@
// KING
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
}
function hate_your_guts()
{
print("\n");
print("\n");
print("OVER ONE THIRD OF THE POPULATION HAS DIED SINCE YOU\n");
print("WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)\n");
print("HATE YOUR GUTS.\n");
}
// Main program
async function main()
{
print(tab(34) + "KING\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
print("\n");
print("\n");
print("DO YOU WANT INSTRUCTIONS");
str = await input();
n5 = 8;
if (str == "AGAIN") {
while (1) {
print("HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED");
x5 = parseInt(await input());
if (x5 == 0)
return;
if (x5 < 8)
break;
print(" COME ON, YOUR TERM IN OFFICE IS ONLY " + n5 + " YEARS.\n");
}
print("HOW MUCH DID YOU HAVE IN THE TREASURY");
a = parseInt(await input());
if (a < 0)
return;
print("HOW MANY COUNTRYMEN");
b = parseInt(await input());
if (b < 0)
return;
print("HOW MANY WORKERS");
c = parseInt(await input());
if (c < 0)
return;
while (1) {
print("HOW MANY SQUARE MILES OF LAND");
d = parseInt(await input());
if (d < 0)
return;
if (d > 1000 && d <= 2000)
break;
print(" COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND\n");
print(" AND 10,000 SQ. MILES OF FOREST LAND.\n");
}
} else {
if (str.substr(0, 1) != "N") {
print("\n");
print("\n");
print("\n");
print("CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS\n");
print("DETINU, A SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR\n");
print("JOB IS TO DECIDE UPON THE CONTRY'S BUDGET AND DISTRIBUTE\n");
print("MONEY TO YOUR COUNTRYMEN FROM THE COMMUNAL TREASURY.\n");
print("THE MONEY SYSTEM IS RALLODS, AND EACH PERSON NEEDS 100\n");
print("RALLODS PER YEAR TO SURVIVE. YOUR COUNTRY'S INCOME COMES\n");
print("FROM FARM PRODUCE AND TOURISTS VISITING YOUR MAGNIFICENT\n");
print("FORESTS, HUNTING, FISHING, ETC. HALF YOUR LAND IS FARM LAND\n");
print("WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD\n");
print("TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT\n");
print("THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER\n");
print("SQUARE MILE TO PLANT.\n");
print("YOUR GOAL IS TO COMPLETE YOUR " + n5 + " YEAR TERM OF OFFICE.\n");
print("GOOD LUCK!\n");
}
print("\n");
a = Math.floor(60000 + (1000 * Math.random()) - (1000 * Math.random()));
b = Math.floor(500 + (10 * Math.random()) - (10 * Math.random()));
c = 0;
d = 2000;
x5 = 0;
}
v3 = 0;
b5 = 0;
x = false;
while (1) {
w = Math.floor(10 * Math.random() + 95);
print("\n");
print("YOU NOW HAVE " + a + " RALLODS IN THE TREASURY.\n");
print(b + " COUNTRYMEN, ");
v9 = Math.floor(((Math.random() / 2) * 10 + 10));
if (c != 0)
print(c + " FOREIGN WORKERS, ");
print("AND " + Math.floor(d) + " SQ. MILES OF LAND.\n");
print("THIS YEAR INDUSTRY WILL BUY LAND FOR " + w + " ");
print("RALLODS PER SQUARE MILE.\n");
print("LAND CURRENTLY COSTS " + v9 + " RALLODS PER SQUARE MILE TO PLANT.\n");
print("\n");
while (1) {
print("HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY");
h = parseInt(await input());
if (h < 0)
continue;
if (h <= d - 1000)
break;
print("*** THINK AGAIN. YOU ONLY HAVE " + (d - 1000) + " SQUARE MILES OF FARM LAND.\n");
if (x == false) {
print("\n");
print("(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE\n");
print("FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES,\n");
print("THICKER TOP SOIL, ETC.)\n");
x = true;
}
}
d = Math.floor(d - h);
a = Math.floor(a + (h * w));
while (1) {
print("HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN");
i = parseInt(await input());
if (i < 0)
continue;
if (i < a)
break;
if (i == a) {
j = 0;
k = 0;
a = 0;
break;
}
print(" THINK AGAIN. YOU'VE ONLY " + a + " RALLODS IN THE TREASURY\n");
}
if (a) {
a = Math.floor(a - i);
while (1) {
print("HOW MANY SQUARE MILES DO YOU WISH TO PLANT");
j = parseInt(await input());
if (j < 0)
continue;
if (j <= b * 2) {
if (j <= d - 1000) {
u1 = Math.floor(j * v9);
if (u1 > a) {
print(" THINK AGAIN. YOU'VE ONLY " + a + " RALLODS LEFT IN THE TREASURY.\n");
continue;
} else if (u1 == a) {
k = 0;
a = 0;
}
break;
}
print(" SORRY, BUT YOU'VE ONLY " + (d - 1000) + " SQ. MILES OF FARM LAND.\n");
continue;
}
print(" SORRY, BUT EACH COUNTRYMAN CAN ONLY PLANT 2 SQ. MILES.\n");
}
}
if (a) {
a -= u1;
while (1) {
print("HOW MANY RALLODS DO YOU WISH TO SPEND ON POLLUTION CONTROL");
k = parseInt(await input());
if (k < 0)
continue;
if (k <= a)
break;
print(" THINK AGAIN. YOU ONLY HAVE " + a + " RALLODS REMAINING.\n");
}
}
if (h == 0 && i == 0 && j == 0 && k == 0) {
print("GOODBYE.\n");
print("(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER\n");
print("'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START\n");
print("OF THE GAME).\n");
return;
}
print("\n");
print("\n");
a = Math.floor(a - k);
a4 = a;
if (Math.floor(i / 100 - b) < 0) {
if (i / 100 < 50) {
hate_your_guts();
break;
}
print(Math.floor(b - (i / 100)) + " COUNTRYMEN DIED OF STARVATION\n");
}
f1 = Math.floor(Math.random() * (2000 - d));
if (k >= 25)
f1 = Math.floor(f1 / (k / 25));
if (f1 > 0)
print(f1 + " COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION\n");
funeral = false;
if (Math.floor((i / 100) - b) >= 0) {
if (f1 > 0) {
print(" YOU WERE FORCED TO SPEND " + Math.floor(f1 * 9) + " RALLODS ON ");
print("FUNERAL EXPENSES.\n");
b5 = f1;
a = Math.floor(a - (f1 * 9));
funeral = true;
}
} else {
print(" YOU WERE FORCED TO SPEND " + Math.floor((f1 + (b - (i / 100))) * 9));
print(" RALLODS ON FUNERAL EXPENSES.\n");
b5 = Math.floor(f1 + (b - (i / 100)));
a = Math.floor(a - ((f1 + (b - (i / 100))) * 9));
funeral = true;
}
if (funeral) {
if (a < 0) {
print(" INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD\n");
d = Math.floor(d + (a / w));
a = 0;
}
b = Math.floor(b - b5);
}
c1 = 0;
if (h != 0) {
c1 = Math.floor(h + (Math.random() * 10) - (Math.random() * 20));
if (c <= 0)
c1 += 20;
print(c1 + " WORKERS CAME TO THE COUNTRY AND ");
}
p1 = Math.floor(((i / 100 - b) / 10) + (k / 25) - ((2000 - d) / 50) - (f1 / 2));
print(Math.abs(p1) + " COUNTRYMEN ");
if (p1 >= 0)
print("CAME TO");
else
print("LEFT");
print(" THE ISLAND.\n");
b = Math.floor(b + p1);
c = Math.floor(c + c1);
u2 = Math.floor(((2000 - d) * ((Math.random() + 1.5) / 2)));
if (c != 0) {
print("OF " + Math.floor(j) + " SQ. MILES PLANTED,");
}
if (j <= u2)
u2 = j;
print(" YOU HARVESTED " + Math.floor(j - u2) + " SQ. MILES OF CROPS.\n");
if (u2 != 0 && t1 < 2) {
print(" (DUE TO ");
if (t1 != 0)
print("INCREASED ");
print("AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)\n");
}
q = Math.floor((j - u2) * (w / 2));
print("MAKING " + q + " RALLODS.\n");
a = Math.floor(a + q);
v1 = Math.floor(((b - p1) * 22) + (Math.random() * 500));
v2 = Math.floor((2000 - d) * 15);
print(" YOU MADE " + Math.abs(Math.floor(v1 - v2)) + " RALLODS FROM TOURIST TRADE.\n");
if (v2 != 0 && v1 - v2 < v3) {
print(" DECREASE BECAUSE ");
g1 = 10 * Math.random();
if (g1 <= 2)
print("FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION.\n");
else if (g1 <= 4)
print("AIR POLLUTION IS KILLING GAME BIRD POPULATION.\n");
else if (g1 <= 6)
print("MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION.\n");
else if (g1 <= 8)
print("UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS.\n");
else if (g1 <= 10)
print("HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT.\n");
}
v3 = Math.floor(a + v3); // Probable bug from original game
a = Math.floor(a + v3);
if (b5 > 200) {
print("\n");
print("\n");
print(b5 + " COUNTRYMEN DIED IN ONE YEAR!!!!!\n");
print("DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY\n");
print("BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU\n");
m6 = Math.floor(Math.random() * 10);
if (m6 <= 3)
print("ALSO HAD YOUR LEFT EYE GOUGED OUT!\n");
else if (m6 <= 6)
print("HAVE ALSO GAINED A VERY BAD REPUTATION.\n");
else
print("HAVE ALSO BEEN DECLARED NATIONAL FINK.\n");
print("\n");
print("\n");
return;
}
if (b < 343) {
hate_your_guts();
break;
}
if (a4 / 100 > 5 && b5 - f1 >= 2) {
print("\n");
print("MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID\n");
print("NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED\n");
print("OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE\n");
print("BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE.\n");
print("THE CHOICE IS YOURS.\n");
print("IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER\n");
print("BEFORE PROCEEDING.\n");
print("\n");
print("\n");
return;
}
if (c > b) {
print("\n");
print("\n");
print("THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER\n");
print("OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND\n");
print("TAKEN OVER THE COUNTRY.\n");
break;
}
if (n5 - 1 == x5) {
print("\n");
print("\n");
print("CONGRATULATIONS!!!!!!!!!!!!!!!!!!\n");
print("YOU HAVE SUCCESFULLY COMPLETED YOUR " + n5 + " YEAR TERM\n");
print("OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT\n");
print("NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD\n");
print("LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT\n");
print("PLAYS THIS GAME.\n");
print("\n");
print("\n");
return;
}
x5++;
b5 = 0;
}
if (Math.random() <= 0.5) {
print("YOU HAVE BEEN ASSASSINATED.\n");
} else {
print("YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW\n");
print("RESIDING IN PRISON.\n");
}
print("\n");
print("\n");
}
main();

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>LEM</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<script src="lem.js"></script>
</body>
</html>

View File

@@ -0,0 +1,326 @@
// LEM
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
}
// Main program
async function main()
{
print(tab(34) + "LEM\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
// ROCKT2 is an interactive game that simulates a lunar
// landing is similar to that of the Apollo program.
// There is absolutely no chance involved
zs = "GO";
b1 = 1;
while (1) {
m = 17.95;
f1 = 5.25;
n = 7.5;
r0 = 926;
v0 = 1.29;
t = 0;
h0 = 60;
r = r0 + h0;
a = -3,425;
r1 = 0;
a1 = 8.84361e-4;
r3 = 0;
a3 = 0;
m1 = 7.45;
m0 = m1;
b = 750;
t1 = 0;
f = 0;
p = 0;
n = 1;
m2 = 0;
s = 0;
c = 0;
if (zs == "YES") {
print("\n");
print("OK, DO YOU WANT THE COMPLETE INSTRUCTIONS OR THE INPUT -\n");
print("OUTPUT STATEMENTS?\n");
while (1) {
print("1=COMPLETE INSTRUCTIONS\n");
print("2=INPUT-OUTPUT STATEMENTS\n");
print("3=NEITHER\n");
b1 = parseInt(await input());
qs = "NO";
if (b1 == 1)
break;
qs = "YES";
if (b1 == 2 || b1 == 3)
break;
}
} else {
print("\n");
print("LUNAR LANDING SIMULATION\n");
print("\n");
print("HAVE YOU FLOWN AN APOLLO/LEM MISSION BEFORE");
while (1) {
print(" (YES OR NO)");
qs = await input();
if (qs == "YES" || qs == "NO")
break;
print("JUST ANSWER THE QUESTION, PLEASE, ");
}
}
if (qs == "YES") {
print("\n");
print("INPUT MEASUREMENT OPTION NUMBER");
} else {
print("\n");
print("WHICH SYSTEM OF MEASUREMENT DO YOU PREFER?\n");
print(" 1=METRIC 0=ENGLISH\n");
print("ENTER THE APPROPIATE NUMBER");
}
while (1) {
k = parseInt(await input());
if (k == 0 || k == 1)
break;
print("ENTER THE APPROPIATE NUMBER");
}
if (k == 1) {
z = 1852.8;
ms = "METERS";
g3 = 3.6;
ns = " KILOMETERS";
g5 = 1000;
} else {
z = 6080;
ms = "FEET";
g3 = 0.592;
ns = "N.MILES";
g5 = z;
}
if (b1 != 3) {
if (qs != "YES") {
print("\n");
print(" YOU ARE ON A LUNAR LANDING MISSION. AS THE PILOT OF\n");
print("THE LUNAR EXCURSION MODULE, YOU WILL BE EXPECTED TO\n");
print("GIVE CERTAIN COMMANDS TO THE MODULE NAVIGATION SYSTEM.\n");
print("THE ON-BOARD COMPUTER WILL GIVE A RUNNING ACCOUNT\n");
print("OF INFORMATION NEEDED TO NAVIGATE THE SHIP.\n");
print("\n");
print("\n");
print("THE ATTITUDE ANGLE CALLED FOR IS DESCRIBED AS FOLLOWS.\n");
print("+ OR -180 DEGREES IS DIRECTLY AWAY FROM THE MOON\n");
print("-90 DEGREES IS ON A TANGENT IN THE DIRECTION OF ORBIT\n");
print("+90 DEGREES IS ON A TANGENT FROM THE DIRECTION OF ORBIT\n");
print("0 (ZERO) DEGREES IS DIRECTLY TOWARD THE MOON\n");
print("\n");
print(tab(30) + "-180|+180\n");
print(tab(34) + "^\n");
print(tab(27) + "-90 < -+- > +90\n");
print(tab(34) + "!\n");
print(tab(34) + "0\n");
print(tab(21) + "<<<< DIRECTION OF ORBIT <<<<\n");
print("\n");
print(tab(20) + "------ SURFACE OF MOON ------\n");
print("\n");
print("\n");
print("ALL ANGLES BETWEEN -180 AND +180 DEGREES ARE ACCEPTED.\n");
print("\n");
print("1 FUEL UNIT = 1 SEC. AT MAX THRUST\n");
print("ANY DISCREPANCIES ARE ACCOUNTED FOR IN THE USE OF FUEL\n");
print("FOR AN ATTITUDE CHANGE.\n");
print("AVAILABLE ENGINE POWER: 0 (ZERO) AND ANY VALUE BETWEEN\n");
print("10 AND 100 PERCENT.\n");
print("\n");
print("NEGATIVE THRUST OR TIME IS PROHIBITED.\n");
print("\n");
}
print("\n");
print("INPUT: TIME INTERVAL IN SECONDS ------ (T)\n");
print(" PERCENTAGE OF THRUST ---------- (P)\n");
print(" ATTITUDE ANGLE IN DEGREES ----- (A)\n");
print("\n");
if (qs != "YES") {
print("FOR EXAMPLE:\n");
print("T,P,A? 10,65,-60\n");
print("TO ABORT THE MISSION AT ANY TIME, ENTER 0,0,0\n");
print("\n");
}
print("OUTPUT: TOTAL TIME IN ELAPSED SECONDS\n");
print(" HEIGHT IN " + ms + "\n");
print(" DISTANCE FROM LANDING SITE IN " + ms + "\n");
print(" VERTICAL VELOCITY IN " + ms + "/SECOND\n");
print(" HORIZONTAL VELOCITY IN " + ms + "/SECOND\n");
print(" FUEL UNITS REMAINING\n");
print("\n");
}
while (1) {
for (i = 1; i <= n; i++) {
if (m1 != 0) {
m1 -= m2;
if (m1 <= 0) {
f = f * (1 + m1 / m2);
m2 = m1 + m2;
print("YOU ARE OUT OF FUEL.\n");
m1 = 0;
}
} else {
f = 0;
m2 = 0;
}
m = m - 0.5 * m2;
r4 = r3;
r3 = -0.5 * r0 * Math.pow(v0 / r, 2) + r * a1 * a1;
r2 = (3 * r3 - r4) / 2 + 0.00526 * f1 * f * c / m;
a4 = a3;
a3 = -2 * r1 * a1 / r;
a2 = (3 * a3 - a4) / 2 + 0.0056 * f1 * f * s / (m * r);
x = r1 * t1 + 0.5 * r2 * t1 * t1;
r = r + x;
h0 = h0 + x;
r1 = r1 + r2 * t1;
a = a + a1 * t1 + 0.5 * a2 * t1 * t1;
a1 = a1 + a2 * t1;
m = m - 0.5 * m2;
t = t + t1;
if (h0 < 3.287828e-4)
break;
}
h = h0 * z;
h1 = r1 * z;
d = r0 * a * z;
d1 = r * a1 * z;
t2 = m1 * b / m0;
print(" " + t + "\t" + h + "\t" + d + "\t" + h1 + "\t" + d1 + "\t" + t2 + "\n");
if (h0 < 3.287828e-4) {
if (r1 < -8.21957e-4 || Math.abs(r * a1) > 4.93174e-4 || h0 < -3.287828e-4) {
print("\n");
print("CRASH !!!!!!!!!!!!!!!!\n");
print("YOUR IMPACT CREATED A CRATER " + Math.abs(h) + " " + ms + " DEEP.\n");
x1 = Math.sqrt(d1 * d1 + h1 * h1) * g3;
print("AT CONTACT YOU WERE TRAVELING " + x1 + " " + ns + "/HR\n");
break;
}
if (Math.abs(d) > 10 * z) {
print("YOU ARE DOWN SAFELY - \n");
print("\n");
print("BUT MISSED THE LANDING SITE BY " + Math.abs(d / g5) + " " + ns + ".\n");
break;
}
print("\n");
print("TRANQUILITY BASE HERE -- THE EAGLE HAS LANDED.\n");
print("CONGRATULATIONS -- THERE WAS NO SPACECRAFT DAMAGE.\n");
print("YOU MAY NOW PROCEED WITH SURFACE EXPLORATION.\n");
break;
}
if (r0 * a > 164.474) {
print("\n");
print("YOU HAVE BEEN LOST IN SPACE WITH NO HOPE OF RECOVERY.\n");
break;
}
if (m1 > 0) {
while (1) {
print("T,P,A");
str = await input();
t1 = parseFloat(str);
f = parseFloat(str.substr(str.indexOf(",") + 1));
p = parseFloat(str.substr(str.lastIndexOf(",") + 1));
f = f / 100;
if (t1 < 0) {
print("\n");
print("THIS SPACECRAFT IS NOT ABLE TO VIOLATE THE SPACE-");
print("TIME CONTINUUM.\n");
print("\n");
} else if (t1 == 0) {
break;
} else if (Math.abs(f - 0.05) > 1 || Math.abs(f - 0.05) < 0.05) {
print("IMPOSSIBLE THRUST VALUE ");
if (f < 0) {
print("NEGATIVE\n");
} else if (f - 0.05 < 0.05) {
print("TOO SMALL\n");
} else {
print("TOO LARGE\n");
}
print("\n");
} else if (Math.abs(p) > 180) {
print("\n");
print("IF YOU WANT TO SPIN AROUND, GO OUTSIDE THE MODULE\n");
print("FOR AN E.V.A.\n");
print("\n");
} else {
break;
}
}
if (t1 == 0) {
print("\n");
print("MISSION ABENDED\n");
break;
}
} else {
t1 = 20;
f = 0;
p = 0;
}
n = 20;
if (t1 >= 400)
n = t1 / 20;
t1 = t1 / n;
p = p * 3.14159 / 180;
s = Math.sin(p);
c = Math.cos(p);
m2 = m0 * t1 * f / b;
r3 = -0.5 * r0 * Math.pow(v0 / r, 2) + r * a1 * a1;
a3 = -2 * r1 * a1 / r;
}
print("\n");
while (1) {
print("DO YOU WANT TO TRY IT AGAIN (YES/NO)?\n");
zs = await input();
if (zs == "YES" || zs == "NO")
break;
}
if (zs != "YES")
break;
}
print("\n");
print("TOO BAD, THE SPACE PROGRAM HATES TO LOSE EXPERIENCED\n");
print("ASTRONAUTS.\n");
}
main();

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>LUNAR</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<script src="lunar.js"></script>
</body>
</html>

View File

@@ -0,0 +1,191 @@
// LUNAR
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
}
var l;
var t;
var m;
var s;
var k;
var a;
var v;
var i;
var j;
var q;
var g;
var z;
var d;
function formula_set_1()
{
l = l + s;
t = t - s;
m = m - s * k;
a = i;
v = j;
}
function formula_set_2()
{
q = s * k / m;
j = v + g * s + z * (-q - q * q / 2 - Math.pow(q, 3) / 3 - Math.pow(q, 4) / 4 - Math.pow(q, 5) / 5);
i = a - g * s * s / 2 - v * s + z * s * (q / 2 + Math.pow(q, 2) / 6 + Math.pow(q, 3) / 12 + Math.pow(q, 4) / 20 + Math.pow(q, 5) / 30);
}
function formula_set_3()
{
while (s >= 5e-3) {
d = v + Math.sqrt(v * v + 2 * a * (g - z * k / m));
s = 2 * a / d;
formula_set_2();
formula_set_1();
}
}
// Main program
async function main()
{
print(tab(33) + "LUNAR\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
print("\n");
print("\n");
print("THIS IS A COMPUTER SIMULATION OF AN APOLLO LUNAR\n");
print("LANDING CAPSULE.\n");
print("\n");
print("\n");
print("THE ON-BOARD COMPUTER HAS FAILED (IT WAS MADE BY\n");
print("XEROX) SO YOU HAVE TO LAND THE CAPSULE MANUALLY.\n");
while (1) {
print("\n");
print("SET BURN RATE OF RETRO ROCKETS TO ANY VALUE BETWEEN\n");
print("0 (FREE FALL) AND 200 (MAXIMUM BURN) POUNDS PER SECOND.\n");
print("SET NEW BURN RATE EVERY 10 SECONDS.\n");
print("\n");
print("CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,500 LBS.\n");
print("\n");
print("\n");
print("\n");
print("GOOD LUCK\n");
l = 0;
print("\n");
print("SEC\tMI + FT\t\tMPH\tLB FUEL\tBURN RATE\n");
print("\n");
a = 120;
v = 1;
m = 33000;
n = 16500;
g = 1e-3;
z = 1.8;
while (1) {
print(l + "\t" + Math.floor(a) + " + " + Math.floor(5280 * (a - Math.floor(a))) + " \t" + Math.floor(3600 * v * 100) / 100 + "\t" + (m - n) + "\t");
k = parseFloat(await input());
t = 10;
should_exit = false;
while (1) {
if (m - n < 1e-3)
break;
if (t < 1e-3)
break;
s = t;
if (m < n + s * k)
s = (m - n) / k;
formula_set_2();
if (i <= 0) {
formula_set_3();
should_exit = true;
break;
}
if (v > 0) {
if (j < 0) {
do {
w = (1 - m * g / (z * k)) / 2;
s = m * v / (z * k * (w + Math.sqrt(w * w + v / z))) + 0.05;
formula_set_2();
if (i <= 0) {
formula_set_3();
should_exit = true;
break;
}
formula_set_1();
if (j > 0)
break;
} while (v > 0) ;
if (should_exit)
break;
continue;
}
}
formula_set_1();
}
if (should_exit)
break;
if (m - n < 1e-3) {
print("FUEL OUT AT " + l + " SECOND\n");
s = (-v * Math.sqrt(v * v + 2 * a * g)) / g;
v = v + g * s;
l = l + s;
break;
}
}
w = 3600 * v;
print("ON MOON AT " + l + " SECONDS - IMPACT VELOCITY " + w + " MPH\n");
if (w <= 1.2) {
print("PERFECT LANDING!\n");
} else if (w <= 10) {
print("GOOD LANDING (COULD BE BETTER)\n");
} else if (w <= 60) {
print("CRAFT DAMAGE... YOU'RE STRANDED HERE UNTIL A RESCUE\n");
print("PARTY ARRIVES. HOPE YOU HAVE ENOUGH OXYGEN!\n");
} else {
print("SORRY THERE WERE NO SURVIVORS. YOU BLEW IT!\n");
print("IN FACT, YOU BLASTED A NEW LUNAR CRATER " + (w * 0.227) + " FEET DEEP!\n");
}
print("\n");
print("\n");
print("\n");
print("TRY AGAIN??\n");
}
}
main();

View File

@@ -0,0 +1,62 @@
import java.util.Scanner;
public class Number {
public static void main(String[] args) {
printIntro();
int points = 100; //start with 100 points for the user
Scanner scan = new Scanner(System.in);
boolean done = false;
while (!done) {
System.out.print("GUESS A NUMBER FROM 1 TO 5? ");
int g = scan.nextInt();
//Initialize 5 random numbers between 1-5
var r = randomNumber(1);
var s = randomNumber(1);
var t = randomNumber(1);
var u = randomNumber(1);
var v = randomNumber(1);
if (r == g) {
points -= 5;
} else if (s == g) {
points += 5;
} else if (t == g) {
points += points;
} else if (u == g) {
points += 1;
} else if (v == g) {
points -= points * 0.5;
} else {
continue; //Doesn't match any of our random numbers, so just ask for another guess
}
if (points > 500) {
done = true;
} else {
System.out.println("YOU HAVE " + points + " POINTS.");
}
}
System.out.println("!!!!YOU WIN!!!! WITH " + points + " POINTS.\n");
}
private static int randomNumber(int x) {
//Note: 'x' is totally ignored as was in the original basic listing
return (int) (5 * Math.random() + 1);
}
private static void printIntro() {
System.out.println(" NUMBER");
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.println("\n\n\n");
System.out.println("YOU HAVE 100 POINTS. BY GUESSING NUMBERS FROM 1 TO 5, YOU");
System.out.println("CAN GAIN OR LOSE POINTS DEPENDING UPON HOW CLOSE YOU GET TO");
System.out.println("A RANDOM NUMBER SELECTED BY THE COMPUTER.");
System.out.println("\n");
System.out.println("YOU OCCASIONALLY WILL GET A JACKPOT WHICH WILL DOUBLE(!)");
System.out.println("YOUR POINT COUNT. YOU WIN WHEN YOU GET 500 POINTS.");
}
}

View File

@@ -1,69 +0,0 @@
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
public class Number {
public static int points = 0;
public static void printempty() { System.out.println(" "); }
public static void print(String toprint) { System.out.println(toprint); }
public static void main(String[] args) {
print("YOU HAVE 100 POINTS. BY GUESSING NUMBERS FROM 1 TO 5, YOU");
print("CAN GAIN OR LOSE POINTS DEPENDING UPON HOW CLOSE YOU GET TO");
print("A RANDOM NUMBER SELECTED BY THE COMPUTER.");
printempty();
print("YOU OCCASIONALLY WILL GET A JACKPOT WHICH WILL DOUBLE(!)");
print("YOUR POINT COUNT. YOU WIN WHEN YOU GET 500 POINTS.");
printempty();
try {
while (true) {
print("GUESS A NUMBER FROM 1 TO 5");
Scanner numbersc = new Scanner(System.in);
String numberstring = numbersc.nextLine();
int number = Integer.parseInt(numberstring);
if (!(number < 1| number > 5)) {
Random rand = new Random();
int randomNum = rand.nextInt((5 - 1) + 1) + 1;
if (randomNum == number) {
print("YOU HIT THE JACKPOT!!!");
points = points * 2;
} else if(ValueRange.of(randomNum, randomNum + 1).isValidIntValue(number)) {
print("+5");
points = points + 5;
} else if(ValueRange.of(randomNum - 1, randomNum + 2).isValidIntValue(number)) {
print("+1");
points = points + 1;
} else if(ValueRange.of(randomNum - 3, randomNum + 1).isValidIntValue(number)) {
print("-1");
points = points - 1;
} else {
print("-half");
points = (int) (points * 0.5);
}
print("YOU HAVE " + points + " POINTS.");
}
if (points >= 500) {
print("!!!!YOU WIN!!!! WITH " + points + " POINTS.");
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>QUEEN</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<script src="queen.js"></script>
</body>
</html>

View File

@@ -0,0 +1,237 @@
// QUEEN
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
}
var sa = [,81, 71, 61, 51, 41, 31, 21, 11,
92, 82, 72, 62, 52, 42, 32, 22,
103, 93, 83, 73, 63, 53, 43, 33,
114, 104, 94, 84, 74, 64, 54, 44,
125, 115, 105, 95, 85, 75, 65, 55,
136, 126, 116, 106, 96, 86, 76, 66,
147, 137, 127, 117, 107, 97, 87, 77,
158, 148, 138, 128, 118, 108, 98, 88];
var m;
var m1;
var u;
var t;
var u1;
var t1;
function show_instructions()
{
print("WE ARE GOING TO PLAY A GAME BASED ON ONE OF THE CHESS\n");
print("MOVES. OUR QUEEN WILL BE ABLE TO MOVE ONLY TO THE LEFT,\n");
print("DOWN, OR DIAGONALLY DOWN AND TO THE LEFT.\n");
print("\n");
print("THE OBJECT OF THE GAME IS TO PLACE THE QUEEN IN THE LOWER\n");
print("LEFT HAND SQUARE BY ALTERNATING MOVES BETWEEN YOU AND THE\n");
print("COMPUTER. THE FIRST ONE TO PLACE THE QUEEN THERE WINS.\n");
print("\n");
print("YOU GO FIRST AND PLACE THE QUEEN IN ANY ONE OF THE SQUARES\n");
print("ON THE TOP ROW OR RIGHT HAND COLUMN.\n");
print("THAT WILL BE YOUR FIRST MOVE.\n");
print("WE ALTERNATE MOVES.\n");
print("YOU MAY FORFEIT BY TYPING '0' AS YOUR MOVE.\n");
print("BE SURE TO PRESS THE RETURN KEY AFTER EACH RESPONSE.\n");
print("\n");
print("\n");
}
function show_map()
{
print("\n");
for (var a = 0; a <= 7; a++) {
for (var b = 1; b <= 8; b++) {
i = 8 * a + b;
print(" " + sa[i] + " ");
}
print("\n");
print("\n");
print("\n");
}
print("\n");
}
function test_move()
{
m = 10 * t + u;
if (m == 158 || m == 127 || m == 126 || m == 75 || m == 73)
return true;
return false;
}
function random_move()
{
// Random move
z = Math.random();
if (z > 0.6) {
u = u1 + 1;
t = t1 + 1;
} else if (z > 0.3) {
u = u1 + 1;
t = t1 + 2;
} else {
u = u1;
t = t1 + 1;
}
m = 10 * t + u;
}
function computer_move()
{
if (m1 == 41 || m1 == 44 || m1 == 73 || m1 == 75 || m1 == 126 || m1 == 127) {
random_move();
return;
}
for (k = 7; k >= 1; k--) {
u = u1;
t = t1 + k;
if (test_move())
return;
u += k;
if (test_move())
return;
t += k;
if (test_move())
return;
}
random_move();
}
// Main program
async function main()
{
print(tab(33) + "QUEEN\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
print("\n");
print("\n");
while (1) {
print("DO YOU WANT INSTRUCTIONS");
str = await input();
if (str == "YES" || str == "NO")
break;
print("PLEASE ANSWER 'YES' OR 'NO'.\n");
}
if (str == "YES")
show_instructions();
while (1) {
show_map();
while (1) {
print("WHERE WOULD YOU LIKE TO START");
m1 = parseInt(await input());
if (m1 == 0) {
print("\n");
print("IT LOOKS LIKE I HAVE WON BY FORFEIT.\n");
print("\n");
break;
}
t1 = Math.floor(m1 / 10);
u1 = m1 - 10 * t1;
if (u1 == 1 || u1 == t1)
break;
print("PLEASE READ THE DIRECTIONS AGAIN.\n");
print("YOU HAVE BEGUN ILLEGALLY.\n");
print("\n");
}
while (m1) {
if (m1 == 158) {
print("\n");
print("C O N G R A T U L A T I O N S . . .\n");
print("\n");
print("YOU HAVE WON--VERY WELL PLAYED.\n");
print("IT LOOKS LIKE I HAVE MET MY MATCH.\n");
print("THANKS FOR PLAYING--I CAN'T WIN ALL THE TIME.\n");
print("\n");
break;
}
computer_move();
print("COMPUTER MOVES TO SQUARE " + m + "\n");
if (m == 158) {
print("\n");
print("NICE TRY, BUT IT LOOKS LIKE I HAVE WON.\n");
print("THANKS FOR PLAYING.\n");
print("\n");
break;
}
print("WHAT IS YOUR MOVE");
while (1) {
m1 = parseInt(await input());
if (m1 == 0)
break;
t1 = Math.floor(m1 / 10);
u1 = m1 - 10 * t1;
p = u1 - u;
l = t1 - t;
if (m1 <= m || p == 0 && l <= 0 || p != 0 && l != p && l != 2 * p) {
print("\n");
print("Y O U C H E A T . . . TRY AGAIN");
continue;
}
break;
}
if (m1 == 0) {
print("\n");
print("IT LOOKS LIKE I HAVE WON BY FORFEIT.\n");
print("\n");
break;
}
}
while (1) {
print("ANYONE ELSE CARE TO TRY");
str = await input();
print("\n");
if (str == "YES" || str == "NO")
break;
print("PLEASE ANSWER 'YES' OR 'NO'.\n");
}
if (str != "YES")
break;
}
print("\n");
print("OK --- THANKS AGAIN.\n");
}
main();

View File

@@ -24,7 +24,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 715,
"execution_count": 727,
"metadata": {},
"outputs": [],
"source": [
@@ -33,7 +33,7 @@
},
{
"cell_type": "code",
"execution_count": 716,
"execution_count": 728,
"metadata": {},
"outputs": [],
"source": [
@@ -57,7 +57,7 @@
},
{
"cell_type": "code",
"execution_count": 717,
"execution_count": 729,
"metadata": {},
"outputs": [],
"source": [
@@ -80,14 +80,14 @@
},
{
"cell_type": "code",
"execution_count": 718,
"execution_count": 730,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"8 4\n"
"5 3\n"
]
}
],
@@ -98,7 +98,7 @@
},
{
"cell_type": "code",
"execution_count": 719,
"execution_count": 731,
"metadata": {},
"outputs": [],
"source": [
@@ -155,14 +155,14 @@
},
{
"cell_type": "code",
"execution_count": 720,
"execution_count": 732,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"BATTLESHIP 5 [(9, 2), (8, 3), (7, 4), (6, 5), (5, 6)]\nCRUISER 3 [(3, 8), (2, 9), (1, 10)]\nDESTROYER<A> 2 [(3, 5), (3, 6)]\nDESTROYER<B> 2 [(6, 5), (7, 4)]\n"
"BATTLESHIP 5 [(10, 2), (10, 3), (10, 4), (10, 5), (10, 6)]\nCRUISER 3 [(9, 4), (8, 4), (7, 4)]\nDESTROYER<A> 2 [(2, 5), (1, 5)]\nDESTROYER<B> 2 [(7, 8), (7, 9)]\n"
]
}
],
@@ -175,7 +175,7 @@
},
{
"cell_type": "code",
"execution_count": 721,
"execution_count": 733,
"metadata": {},
"outputs": [],
"source": [
@@ -186,7 +186,7 @@
},
{
"cell_type": "code",
"execution_count": 722,
"execution_count": 734,
"metadata": {},
"outputs": [
{
@@ -221,7 +221,7 @@
},
{
"cell_type": "code",
"execution_count": 723,
"execution_count": 735,
"metadata": {},
"outputs": [],
"source": [
@@ -232,25 +232,14 @@
},
{
"cell_type": "code",
"execution_count": 724,
"execution_count": 736,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"DESTROYER<B> 2 [(1, 7), (2, 6), (3, 5), (4, 4), (5, 3)]\n",
" 1 2 3 4 5 6 7 8 9 10\n",
" 1 0 \n",
" 2 0 \n",
" 3 0 \n",
" 4 0 \n",
" 5 0 \n",
" 6 \n",
" 7 \n",
" 8 \n",
" 9 \n",
"10 \n"
"DESTROYER<B> 2 [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]\n 1 2 3 4 5 6 7 8 9 10\n 1 0 \n 2 0 \n 3 0 \n 4 0 \n 5 0 \n 6 \n 7 \n 8 \n 9 \n10 \n"
]
}
],
@@ -265,14 +254,24 @@
},
{
"cell_type": "code",
"execution_count": 725,
"execution_count": 737,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
" 1 2 3 4 5 6 7 8 9 10\n 1 1 0 \n 2 1 0 \n 3 1 0 \n 4 0 \n 5 2 0 \n 6 2 \n 7 3 3 \n 8 \n 9 \n10 \n"
" 1 2 3 4 5 6 7 8 9 10\n",
" 1 \n",
" 2 1 \n",
" 3 1 2 \n",
" 4 1 2 \n",
" 5 0 \n",
" 6 0 \n",
" 7 0 \n",
" 8 0 \n",
" 9 0 3 \n",
"10 3 \n"
]
}
],
@@ -305,14 +304,14 @@
},
{
"cell_type": "code",
"execution_count": 726,
"execution_count": 738,
"metadata": {},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[(4, 4), (2, 8), (10, 6), (2, 4), (10, 7)]\n"
"[(4, 1), (3, 6), (6, 10), (10, 6), (4, 5)]\n"
]
}
],

View File

@@ -1,25 +1,121 @@
import re
import random
###################
#
# static variables
#
###################
# declare static variables
BOARD_WIDTH = 10
BOARD_HEIGHT = 10
SHIPS = [("BATTLESHIP", 5),
("CRUISER", 3),
("DESTROYER<A>", 2),
("DESTROYER<B>", 2)]
# game ships
#
# data structure keeping track of information
# about the ships in the game. for each ship,
# the following information is provided:
#
# name - string representation of the ship
# length - number of "parts" on the ship that
# can be shot
# shots - number of shots the ship counts for
SHIPS = [("BATTLESHIP", 5, 3),
("CRUISER", 3, 2),
("DESTROYER<A>", 2, 1),
("DESTROYER<B>", 2, 1)]
VALID_MOVES = [[-1, 0],
[-1, 1],
[0, 1],
[1, 1],
[1, 0],
[1, -1],
[0, -1],
[1, -1]]
VALID_MOVES = [[-1, 0], # North
[-1, 1], # North East
[0, 1], # East
[1, 1], # South East
[1, 0], # South
[1, -1], # South West
[0, -1], # West
[-1, -1]] # North West
COORD_REGEX = '[ \t]{0,}(-?[0-9]{1,3})[ \t]{0,},[ \t]{0,}(-?[0-9]{1,2})'
####################
#
# global variables
#
####################
# array of BOARD_HEIGHT arrays, BOARD_WIDTH in length,
# representing the human player and computer
player_board = []
computer_board = []
# array representing the coordinates
# for each ship for player and computer
# array is in the same order as SHIPS
player_ship_coords = []
computer_ship_coords = []
# keep track of the turn
current_turn = 0
####################################
#
# SHOTS
#
# The number of shots computer/player
# has is determined by the shot "worth"
# of each ship the computer/player
# possesses. As long as the ship has one
# part not hit (i.e., ship was not
# sunk), the player gets all the shots
# from that ship.
# flag indicating if computer's shots are
# printed out during computer's turn
print_computer_shots = False
# keep track of the number
# of available computer shots
# inital shots are 7
num_computer_shots = 7
# keep track of the number
# of available player shots
# initial shots are 7
num_player_shots = 7
#
# SHOTS
#
####################################
# flag indicating whose turn
# it currently is
COMPUTER = 0
PLAYER = 1
active_turn = COMPUTER
####################
#
# game functions
#
####################
# random number functions
#
# seed the random number generator
random.seed()
# random_x_y
#
# generate a valid x,y coordinate on the board
# returns: x,y
# x: integer between 1 and BOARD_HEIGHT
# y: integer between 1 and BOARD WIDTH
def random_x_y():
x = random.randrange(1, BOARD_WIDTH+1)
y = random.randrange(1, BOARD_HEIGHT+1)
return (x, y)
# input_coord
#
@@ -51,32 +147,380 @@ def input_coord():
return x, y
# input_ship_coords
# generate_ship_coordinates
#
# ask the user for coordinates for each
# ship on their board. uses input_coord()
# to read each coord.
# returns an array of arrays, one array for
# each ship's coordinates, which is an array
# of (x,y) sets.
def input_ship_coords():
print("ENTER COORDINATES FOR...")
# given a ship from the SHIPS array, generate
# the coordinates of the ship. the starting point
# of the ship's first coordinate is generated randomly.
# once the starting coordinates are determined, the
# possible directions of the ship, accounting for the
# edges of the board, are determined. once possible
# directions are found, a direction is randomly
# determined and the remaining coordinates are
# generated by adding or substraction from the starting
# coordinates as determined by direction.
#
# arguments:
# ship - index into the SHIPS array
#
# returns:
# array of sets of coordinates (x,y)
def generate_ship_coordinates(ship):
# randomly generate starting x,y coordinates
start_x, start_y = random_x_y()
coords = []
# using starting coordinates and the ship type,
# generate a vector of possible directions the ship
# could be placed. directions are numbered 0-7 along
# points of the compass (N, NE, E, SE, S, SW, W, NW)
# clockwise. a vector of valid directions where the
# ship does not go off the board is determined
ship_len = SHIPS[ship][1] - 1
dirs = [False for x in range(8)]
dirs[0] = (start_x - ship_len) >= 1
dirs[2] = (start_y + ship_len) <= BOARD_WIDTH
dirs[1] = dirs[0] and dirs[2]
dirs[4] = (start_x + ship_len) <= BOARD_HEIGHT
dirs[3] = dirs[2] and dirs[4]
dirs[6] = (start_y - ship_len) >= 1
dirs[5] = dirs[4] and dirs[6]
dirs[7] = dirs[6] and dirs[0]
directions = [p for p in range(len(dirs)) if dirs[p]]
# using the vector of valid directions, pick a
# random direction to place the ship
dir_idx = random.randrange(len(directions))
direction = directions[dir_idx]
# using the starting x,y, direction and ship
# type, return the coordinates of each point
# of the ship. VALID_MOVES is a staic array
# of coordinate offsets to walk from starting
# coordinate to the end coordinate in the
# chosen direction
ship_len = SHIPS[ship][1] - 1
d_x = VALID_MOVES[direction][0]
d_y = VALID_MOVES[direction][1]
coords = [(start_x, start_y)]
x_coord = start_x
y_coord = start_y
for i in range(ship_len):
x_coord = x_coord + d_x
y_coord = y_coord + d_y
coords.append((x_coord, y_coord))
return coords
# create_blank_board
#
# helper function to create a game board
# that is blank
def create_blank_board():
return [[None for y in range(BOARD_WIDTH)]
for x in range(BOARD_HEIGHT)]
# print_board
#
# print out the game board for testing
# purposes
def print_board(board):
# print board header (column numbers)
print(' ', end='')
for z in range(BOARD_WIDTH):
print(f'{z+1:3}', end='')
print('')
for x in range(len(board)):
print(f'{x+1:2}', end='')
for y in range(len(board[x])):
if(board[x][y] is None):
print(f"{' ':3}", end='')
else:
print(f"{board[x][y]:3}", end='')
print('')
# place_ship
#
# place a ship on a given board. updates
# the board's row,column value at the given
# coordinates to indicate where a ship is
# on the board.
#
# inputs: board - array of BOARD_HEIGHT by BOARD_WIDTH
# coords - array of sets of (x,y) coordinates of each
# part of the given ship
# ship - integer repreesnting the type of ship (given in SHIPS)
def place_ship(board, coords, ship):
for coord in coords:
board[coord[0]-1][coord[1]-1] = ship
# NOTE: A little quirk that exists here and in the orginal
# game: Ships are allowed to cross each other!
# For example: 2 destroyers, length 2, one at
# [(1,1),(2,2)] and other at [(2,1),(1,2)]
def generate_board():
board = create_blank_board()
ship_coords = []
for ship in range(len(SHIPS)):
placed = False
coords = []
while not placed:
coords = generate_ship_coordinates(ship)
clear = True
for coord in coords:
if board[coord[0]-1][coord[1]-1] is not None:
clear = False
break
if clear:
placed = True
place_ship(board, coords, ship)
ship_coords.append(coords)
return board, ship_coords
# execute_shot
#
# given a board and x, y coordinates,
# execute a shot. returns True if the shot
# is valid, False if not
def execute_shot(turn, board, x, y):
global current_turn
square = board[x-1][y-1]
ship_hit = -1
if square is not None:
if square >= 0 and square < len(SHIPS):
ship_hit = square
board[x-1][y-1] = 10 + current_turn
return ship_hit
# calculate_shots
#
# function to examine each board
# and determine how many shots remaining
def calculate_shots(board):
ships_found = [0 for x in range(len(SHIPS))]
for x in range(BOARD_HEIGHT):
for y in range(BOARD_WIDTH):
square = board[x-1][y-1]
if square is not None:
if square >= 0 and square < len(SHIPS):
ships_found[square] = 1
shots = 0
for ship in range(len(ships_found)):
if ships_found[ship] == 1:
shots += SHIPS[ship][2]
return shots
# initialize
#
# function to initialize global variables used
# during game play.
def initialize_game():
# initialize the global player and computer
# boards
global player_board
player_board = create_blank_board()
# generate the ships for the computer's
# board
global computer_board
global computer_ship_coords
computer_board, computer_ship_coords = generate_board()
# print out the title 'screen'
print('{0:>38}'.format("SALVO"))
print('{0:>57s}'.format("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"))
print('')
print('{0:>52s}'.format("ORIGINAL BY LAWRENCE SIEGEL, 1973"))
print('{0:>56s}'.format("PYTHON 3 PORT BY TODD KAISER, MARCH 2021"))
print('\n')
# ask the player for ship coordinates
print("ENTER COORDINATES FOR...")
ship_coords = []
for ship in SHIPS:
print(ship[0])
list = []
for i in range(ship[1]):
x, y = input_coord()
list.append((x, y))
coords.append(list)
return coords
ship_coords.append(list)
# add ships to the user's board
for ship in range(len(SHIPS)):
place_ship(player_board, ship_coords[ship], ship)
# see if the player wants the computer's ship
# locations printed out and if the player wants to
# start
input_loop = True
player_start = "YES"
while input_loop:
player_start = input("DO YOU WANT TO START? ")
if player_start == "WHERE ARE YOUR SHIPS?":
for ship in range(len(SHIPS)):
print(SHIPS[ship][0])
coords = computer_ship_coords[ship]
for coord in coords:
x = coord[0]
y = coord[1]
print('{0:2}'.format(x), '{0:2}'.format(y))
else:
input_loop = False
# ask the player if they want the computer's shots
# printed out each turn
global print_computer_shots
see_computer_shots = input("DO YOU WANT TO SEE MY SHOTS? ")
if see_computer_shots.lower() == "yes":
print_computer_shots = True
global first_turn
global second_turn
if player_start.lower() != "yes":
first_turn = COMPUTER
second_turn = PLAYER
# calculate the initial number of shots for each
global num_computer_shots
global num_player_shots
num_player_shots = calculate_shots(player_board)
num_computer_shots = calculate_shots(computer_board)
# print out the title 'screen'
print('{0:>38}'.format("SALVO"))
print('{0:>57s}'.format("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"))
print('\n\n')
####################################
#
# Turn Control
#
# define functions for executing the turns for
# the player and the computer. By defining this as
# functions, we can easily start the game with
# either computer or player and alternate back and
# forth, replicating the gotos in the original game
# ask the user for ship coordinates
coords = input_ship_coords()
# initialize the first_turn function to the
# player's turn
first_turn = PLAYER
# initialize the second_turn to the computer's
# turn
second_turn = COMPUTER
def execute_turn(turn):
global num_computer_shots
global num_player_shots
# print out the number of shots the current
# player has
board = None
num_shots = 0
if turn == COMPUTER:
print("I HAVE", num_computer_shots, "SHOTS.")
board = player_board
num_shots = num_computer_shots
else:
print("YOU HAVE", num_player_shots, "SHOTS.")
board = computer_board
num_shots = num_player_shots
shots = []
for shot in range(num_shots):
valid_shot = False
x = -1
y = -1
# loop until we have a valid shot. for the
# computer, we randomly pick a shot. for the
# player we request shots
while not valid_shot:
if turn == COMPUTER:
x, y = random_x_y()
else:
x, y = input_coord()
square = board[x-1][y-1]
if square is not None:
if square > 10:
if turn == PLAYER:
print("YOU SHOT THERE BEFORE ON TURN", square - 10)
continue
shots.append((x, y))
valid_shot = True
hits = []
for shot in shots:
hit = execute_shot(turn, board, shot[0], shot[1])
if hit >= 0:
hits.append(hit)
if turn == COMPUTER:
if print_computer_shots:
print(shot[0], shot[1])
for hit in hits:
if turn == COMPUTER:
print("I HIT YOUR", SHIPS[hit][0])
else:
print("YOU HIT MY", SHIPS[hit][0])
if turn == COMPUTER:
num_player_shots = calculate_shots(board)
return num_player_shots
else:
num_computer_shots = calculate_shots(board)
return num_computer_shots
#
# Turn Control
#
######################################
######################
#
# main game flow
#
######################
# initialize the player and computer
# boards
initialize_game()
# execute turns until someone wins or we run
# out of squares to shoot
game_over = False
while not game_over:
# increment the turn
current_turn = current_turn + 1
print("\n")
print("TURN", current_turn)
# print("computer")
# print_board(computer_board)
# print("player")
# print_board(player_board)
if execute_turn(first_turn) == 0:
game_over = True
continue
if execute_turn(second_turn) == 0:
game_over = True
continue

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>QUBIT</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<script src="qubit.js"></script>
</body>
</html>

View File

@@ -0,0 +1,448 @@
// QUBIT
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
}
var xa = [];
var la = [];
var ma = [[],
[,1,2,3,4], // 1
[,5,6,7,8], // 2
[,9,10,11,12], // 3
[,13,14,15,16], // 4
[,17,18,19,20], // 5
[,21,22,23,24], // 6
[,25,26,27,28], // 7
[,29,30,31,32], // 8
[,33,34,35,36], // 9
[,37,38,39,40], // 10
[,41,42,43,44], // 11
[,45,46,47,48], // 12
[,49,50,51,52], // 13
[,53,54,55,56], // 14
[,57,58,59,60], // 15
[,61,62,63,64], // 16
[,1,17,33,49], // 17
[,5,21,37,53], // 18
[,9,25,41,57], // 19
[,13,29,45,61], // 20
[,2,18,34,50], // 21
[,6,22,38,54], // 22
[,10,26,42,58], // 23
[,14,30,46,62], // 24
[,3,19,35,51], // 25
[,7,23,39,55], // 26
[,11,27,43,59], // 27
[,15,31,47,63], // 28
[,4,20,36,52], // 29
[,8,24,40,56], // 30
[,12,28,44,60], // 31
[,16,32,48,64], // 32
[,1,5,9,13], // 33
[,17,21,25,29], // 34
[,33,37,41,45], // 35
[,49,53,57,61], // 36
[,2,6,10,14], // 37
[,18,22,26,30], // 38
[,34,38,42,46], // 39
[,50,54,58,62], // 40
[,3,7,11,15], // 41
[,19,23,27,31], // 42
[,35,39,43,47], // 43
[,51,55,59,63], // 44
[,4,8,12,16], // 45
[,20,24,28,32], // 46
[,36,40,44,48], // 47
[,52,56,60,64], // 48
[,1,6,11,16], // 49
[,17,22,27,32], // 50
[,33,38,43,48], // 51
[,49,54,59,64], // 52
[,13,10,7,4], // 53
[,29,26,23,20], // 54
[,45,42,39,36], // 55
[,61,58,55,52], // 56
[,1,21,41,61], // 57
[,2,22,42,62], // 58
[,3,23,43,63], // 59
[,4,24,44,64], // 60
[,49,37,25,13], // 61
[,50,38,26,14], // 62
[,51,39,27,15], // 63
[,52,40,28,16], // 64
[,1,18,35,52], // 65
[,5,22,39,56], // 66
[,9,26,43,60], // 67
[,13,30,47,64], // 68
[,49,34,19,4], // 69
[,53,38,23,8], // 70
[,57,42,27,12], // 71
[,61,46,31,16], // 72
[,1,22,43,64], // 73
[,16,27,38,49], // 74
[,4,23,42,61], // 75
[,13,26,39,52] // 76
];
var ya = [,1,49,52,4,13,61,64,16,22,39,23,38,26,42,27,43];
function show_board()
{
for (xx = 1; xx <= 9; xx++)
print("\n");
for (i = 1; i <= 4; i++) {
for (j = 1; j <= 4; j++) {
str = "";
for (i1 = 1; i1 <= j; i1++)
str += " ";
for (k = 1; k <= 4; k++) {
q = 16 * i + 4 * j + k - 20;
if (xa[q] == 0)
str += "( ) ";
if (xa[q] == 5)
str += "(M) ";
if (xa[q] == 1)
str += "(Y) ";
if (xa[q] == 1 / 8)
str += "( ) ";
}
print(str + "\n");
print("\n");
}
print("\n");
print("\n");
}
}
function process_board()
{
for (i = 1; i <= 64; i++) {
if (xa[i] == 1 / 8)
xa[i] = 0;
}
}
function check_for_lines()
{
for (s = 1; s <= 76; s++) {
j1 = ma[s][1];
j2 = ma[s][2];
j3 = ma[s][3];
j4 = ma[s][4];
la[s] = xa[j1] + xa[j2] + xa[j3] + xa[j4];
}
}
function show_square(m)
{
k1 = Math.floor((m - 1) / 16) + 1;
j2 = m - 16 * (k1 - 1);
k2 = Math.floor((j2 - 1) / 4) + 1;
k3 = m - (k1 - 1) * 16 - (k2 - 1) * 4;
m = k1 * 100 + k2 * 10 + k3;
print(" " + m + " ");
}
function select_move() {
if (i % 4 <= 1) {
a = 1;
} else {
a = 2;
}
for (j = a; j <= 5 - a; j += 5 - 2 * a) {
if (xa[ma[i][j]] == s)
break;
}
if (j > 5 - a)
return false;
xa[ma[i][j]] = s;
m = ma[i][j];
print("MACHINE TAKES");
show_square(m);
return true;
}
// Main control section
async function main()
{
print(tab(33) + "QUBIC\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
print("\n");
print("\n");
while (1) {
print("DO YOU WANT INSTRUCTIONS");
str = await input();
str = str.substr(0, 1);
if (str == "Y" || str == "N")
break;
print("INCORRECT ANSWER. PLEASE TYPE 'YES' OR 'NO'");
}
if (str == "Y") {
print("\n");
print("THE GAME IS TIC-TAC-TOE IN A 4 X 4 X 4 CUBE.\n");
print("EACH MOVE IS INDICATED BY A 3 DIGIT NUMBER, WITH EACH\n");
print("DIGIT BETWEEN 1 AND 4 INCLUSIVE. THE DIGITS INDICATE THE\n");
print("LEVEL, ROW, AND COLUMN, RESPECTIVELY, OF THE OCCUPIED\n");
print("PLACE. \n");
print("\n");
print("TO PRINT THE PLAYING BOARD, TYPE 0 (ZERO) AS YOUR MOVE.\n");
print("THE PROGRAM WILL PRINT THE BOARD WITH YOUR MOVES INDI-\n");
print("CATED WITH A (Y), THE MACHINE'S MOVES WITH AN (M), AND\n");
print("UNUSED SQUARES WITH A ( ). OUTPUT IS ON PAPER.\n");
print("\n");
print("TO STOP THE PROGRAM RUN, TYPE 1 AS YOUR MOVE.\n");
print("\n");
print("\n");
}
while (1) {
for (i = 1; i <= 64; i++)
xa[i] = 0;
z = 1;
print("DO YOU WANT TO MOVE FIRST");
while (1) {
str = await input();
str = str.substr(0, 1);
if (str == "Y" || str == "N")
break;
print("INCORRECT ANSWER. PLEASE TYPE 'YES' OR 'NO'");
}
while (1) {
while (1) {
print(" \n");
print("YOUR MOVE");
j1 = parseInt(await input());
if (j1 == 0) {
show_board();
continue;
}
if (j1 == 1)
return;
k1 = Math.floor(j1 / 100);
j2 = j1 - k1 * 100;
k2 = Math.floor(j2 / 10);
k3 = j2 - k2 * 10;
m = 16 * k1 + 4 * k2 + k3 - 20;
if (k1 < 1 || k2 < 1 || k3 < 1 || k1 > 4 || k2 > 4 || k3 >> 4) {
print("INCORRECT MOVE, RETYPE IT--");
} else {
process_board();
if (xa[m] != 0) {
print("THAT SQUARE IS USED, TRY AGAIN.\n");
} else {
break;
}
}
}
xa[m] = 1;
check_for_lines();
status = 0;
for (j = 1; j <= 3; j++) {
for (i = 1; i <= 76; i++) {
if (j == 1) {
if (la[i] != 4)
continue;
print("YOU WIN AS FOLLOWS");
for (j = 1; j <= 4; j++) {
m = ma[i][j];
show_square(m);
}
status = 1;
break;
}
if (j == 2) {
if (la[i] != 15)
continue;
for (j = 1; j <= 4; j++) {
m = ma[i][j];
if (xa[m] != 0)
continue;
xa[m] = 5;
print("MACHINE MOVES TO ");
show_square(m);
}
print(", AND WINS AS FOLLOWS");
for (j = 1; j <= 4; j++) {
m = ma[i][j];
show_square(m);
}
status = 1;
break;
}
if (j == 3) {
if (la[i] != 3)
continue;
print("NICE TRY, MACHINE MOVES TO");
for (j = 1; j <= 4; j++) {
m = ma[i][j];
if (xa[m] != 0)
continue;
xa[m] = 5;
show_square(m);
status = 2;
}
break;
}
}
if (i <= 76)
break;
}
if (status == 2)
continue;
if (status == 1)
break;
// x = x; non-useful in original
i = 1;
do {
la[i] = xa[ma[i][1]] + xa[ma[i][2]] + xa[ma[i][3]] + xa[ma[i][4]];
l = la[i];
if (l == 10) {
for (j = 1; j <= 4; j++) {
if (xa[ma[i][j]] == 0)
xa[ma[i][j]] = 1 / 8;
}
}
} while (++i <= 76) ;
check_for_lines();
i = 1;
do {
if (la[i] == 0.5) {
s = 1 / 8;
select_move();
break;
}
if (la[i] == 5 + 3 / 8) {
s = 1 / 8;
select_move();
break;
}
} while (++i <= 76) ;
if (i <= 76)
continue;
process_board();
i = 1;
do {
la[i] = xa[ma[i][1]] + xa[ma[i][2]] + xa[ma[i][3]] + xa[ma[i][4]];
l = la[i];
if (l == 2) {
for (j = 1; j <= 4; j++) {
if (xa[ma[i][j]] == 0)
xa[ma[i][j]] = 1 / 8;
}
}
} while (++i <= 76) ;
check_for_lines();
i = 1;
do {
if (la[i] == 0.5) {
s = 1 / 8;
select_move();
break;
}
if (la[i] == 1 + 3 / 8) {
s = 1 / 8;
select_move();
break;
}
} while (++i <= 76) ;
if (i <= 76)
continue;
for (k = 1; k <= 18; k++) {
p = 0;
for (i = 4 * k - 3; i <= 4 * k; i++) {
for (j = 1; j <= 4; j++)
p += xa[ma[i][j]];
}
if (p == 4 || p == 9) {
s = 1 / 8;
for (i = 4 * k - 3; i <= 4 * k; i++) {
if (select_move())
break;
}
s = 0;
}
}
if (k <= 18)
continue
process_board();
z = 1;
do {
if (xa[ya[z]] == 0)
break;
} while (++z < 17) ;
if (z >= 17) {
for (i = 1; i <= 64; i++) {
if (xa[i] == 0) {
xa[i] = 5;
m = i;
print("MACHINE LIKES");
break;
}
}
if (i > 64) {
print("THE GAME IS A DRAW.\n");
break;
}
} else {
m = ya[z];
xa[m] = 5;
print("MACHINE MOVES TO");
}
show_square(m);
}
print(" \n");
print("DO YOU WANT TO TRY ANOTHER GAME");
while (1) {
str = await input();
str = str.substr(0, 1);
if (str == "Y" || str == "N")
break;
print("INCORRECT ANSWER. PLEASE TYPE 'YES' OR 'NO'");
}
if (str == "N")
break;
}
}
main();

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>TIC TAC TOE 1</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<script src="tictactoe1.js"></script>
</body>
</html>

View File

@@ -0,0 +1,147 @@
// TIC TAC TOE 1
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
}
function mf(x)
{
return x - 8 * Math.floor((x - 1) / 8);
}
function computer_moves()
{
print("COMPUTER MOVES " + m + "\n");
}
var m;
// Main control section
async function main()
{
print(tab(30) + "TIC TAC TOE\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
print("\n");
print("\n");
//
// This program plays Tic Tac Toe
// The machine goes first
print("THE GAME BOARD IS NUMBERED:\n");
print("\n");
print("1 2 3\n");
print("8 9 4\n");
print("7 6 5\n");
print("\n");
//
// Main program
while (1) {
print("\n");
print("\n");
a = 9;
m = a;
computer_moves();
print("YOUR MOVE");
m = parseInt(await input());
p = m;
b = mf(p + 1);
m = b;
computer_moves();
print("YOUR MOVE");
m = parseInt(await input());
q = m;
if (q != mf(b + 4)) {
c = mf(b + 4);
m = c;
computer_moves();
print("AND WINS ********\n");
continue;
}
c = mf(b + 2);
m = c;
computer_moves();
print("YOUR MOVE");
m = parseInt(await input());
r = m;
if (r != mf(c + 4)) {
d = mf(c + 4);
m = d;
computer_moves();
print("AND WINS ********\n");
continue;
}
if (p % 2 == 0) {
d = mf(c + 7);
m = d;
computer_moves();
print("AND WINS ********\n");
continue;
}
d = mf(c + 3);
m = d;
computer_moves();
print("YOUR MOVE");
m = parseInt(await input());
s = m;
if (s != mf(d + 4)) {
e = mf(d + 4);
m = e;
computer_moves();
}
e = mf(d + 6);
m = e;
computer_moves();
print("THE GAME IS A DRAW.\n");
}
}
main();

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>TIC TAC TOE 2</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<script src="tictactoe2.js"></script>
</body>
</html>

View File

@@ -0,0 +1,288 @@
// TIC TAC TOE 2
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
}
var s = [];
function who_win(piece)
{
if (piece == -1) {
print("I WIN, TURKEY!!!\n");
} else if (piece == 1) {
print("YOU BEAT ME!! GOOD GAME.\n");
}
}
function show_board()
{
print("\n");
for (i = 1; i <= 9; i++) {
print(" ");
if (s[i] == -1) {
print(qs + " ");
} else if (s[i] == 0) {
print(" ");
} else {
print(ps + " ");
}
if (i == 3 || i == 6) {
print("\n");
print("---+---+---\n");
} else if (i != 9) {
print("!");
}
}
print("\n");
print("\n");
print("\n");
for (i = 1; i <= 7; i += 3) {
if (s[i] && s[i] == s[i + 1] && s[i] == s[i + 2]) {
who_win(s[i]);
return true;
}
}
for (i = 1; i <= 3; i++) {
if (s[i] && s[i] == s[i + 3] && s[i] == s[i + 6]) {
who_win(s[i]);
return true;
}
}
if (s[1] && s[1] == s[5] && s[1] == s[9]) {
who_win(s[1]);
return true;
}
if (s[3] && s[3] == s[5] && s[3] == s[7]) {
who_win(s[3]);
return true;
}
for (i = 1; i <= 9; i++) {
if (s[i] == 0)
break;
}
if (i > 9) {
print("IT'S A DRAW. THANK YOU.\n");
return true;
}
return false;
}
// Main control section
async function main()
{
print(tab(30) + "TIC-TAC-TOE\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
print("\n");
print("\n");
for (i = 1; i <= 9; i++)
s[i] = 0;
print("THE BOARD IS NUMBERED:\n");
print(" 1 2 3\n");
print(" 4 5 6\n");
print(" 7 8 9\n");
print("\n");
print("\n");
print("\n");
print("DO YOU WANT 'X' OR 'O'");
str = await input();
if (str == "X") {
ps = "X";
qs = "O";
first_time = true;
} else {
ps = "O";
qs = "X";
first_time = false;
}
while (1) {
if (!first_time) {
g = -1;
h = 1;
if (s[5] == 0) {
s[5] = -1;
} else if (s[5] == 1 && s[1] == 0) {
s[1] = -1;
} else if (s[5] != 1 && s[2] == 1 && s[1] == 0 || s[5] != 1 && s[4] == 1 && s[1] == 0) {
s[1] = -1;
} else if (s[5] != 1 && s[6] == 1 && s[9] == 0 || s[5] != 1 && s[8] == 1 && s[9] == 0) {
s[9] = -1;
} else {
while (1) {
played = false;
if (g == 1) {
j = 3 * Math.floor((m - 1) / 3) + 1;
if (3 * Math.floor((m - 1) / 3) + 1 == m)
k = 1;
if (3 * Math.floor((m - 1) / 3) + 2 == m)
k = 2;
if (3 * Math.floor((m - 1) / 3) + 3 == m)
k = 3;
} else {
j = 1;
k = 1;
}
while (1) {
if (s[j] == g) {
if (s[j + 2] == g) {
if (s[j + 1] == 0) {
s[j + 1] = -1;
played = true;
break;
}
} else {
if (s[j + 2] == 0 && s[j + 1] == g) {
s[j + 2] = -1;
played = true;
break;
}
}
} else {
if (s[j] != h && s[j + 2] == g && s[j + 1] == g) {
s[j] = -1;
played = true;
break;
}
}
if (s[k] == g) {
if (s[k + 6] == g) {
if (s[k + 3] == 0) {
s[k + 3] = -1;
played = true;
break;
}
} else {
if (s[k + 6] == 0 && s[k + 3] == g) {
s[k + 6] = -1;
played = true;
break;
}
}
} else {
if (s[k] != h && s[k + 6] == g && s[k + 3] == g) {
s[k] = -1;
played = true;
break;
}
}
if (g == 1)
break;
if (j == 7 && k == 3)
break;
k++;
if (k > 3) {
k = 1;
j += 3;
if (j > 7)
break;
}
}
if (!played) {
if (s[5] == g) {
if (s[3] == g && s[7] == 0) {
s[7] = -1;
played = true;
} else if (s[9] == g && s[1] == 0) {
s[1] = -1;
played = true;
} else if (s[7] == g && s[3] == 0) {
s[3] = -1;
played = true;
} else if (s[9] == 0 && s[1] == g) {
s[9] = -1;
played = true;
}
}
if (!played) {
if (g == -1) {
g = 1;
h = -1;
}
}
}
if (played)
break;
}
if (!played) {
if (s[9] == 1 && s[3] == 0 && s[1] != 1) {
s[3] = -1;
} else {
for (i = 2; i <= 9; i++) {
if (s[i] == 0) {
s[i] = -1;
break;
}
}
if (i > 9) {
s[1] = -1;
}
}
}
}
print("\n");
print("THE COMPUTER MOVES TO...");
if (show_board())
break;
}
first_time = false;
while (1) {
print("\n");
print("WHERE DO YOU MOVE");
m = parseInt(await input());
if (m == 0) {
print("THANKS FOR THE GAME.\n");
break;
}
if (m >= 1 && m <= 9 && s[m] == 0)
break;
print("THAT SQUARE IS OCCUPIED.\n");
print("\n");
print("\n");
}
g = 1;
s[m] = 1;
if (show_board())
break;
}
}
main();

527
90 Tower/java/Tower.java Normal file
View File

@@ -0,0 +1,527 @@
import java.lang.Math;
import java.util.Scanner;
/**
* Game of Tower
* <p>
* Based on the BASIC game of Tower here
* https://github.com/coding-horror/basic-computer-games/blob/main/90%20Tower/tower.bas
* <p>
* Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing
* new features - no additional text, error checking, etc has been added.
*
* Converted from BASIC to Java by Darren Cardenas.
*/
public class Tower {
private final static int MAX_DISK_SIZE = 15;
private final static int MAX_NUM_COLUMNS = 3;
private final static int MAX_NUM_MOVES = 128;
private final static int MAX_NUM_ROWS = 7;
private final Scanner scan; // For user input
// Represent all possible disk positions
private int[][] positions;
private enum Step {
INITIALIZE, SELECT_TOTAL_DISKS, SELECT_DISK_MOVE, SELECT_NEEDLE, CHECK_SOLUTION
}
public Tower() {
scan = new Scanner(System.in);
// Row 0 and column 0 are not used
positions = new int[MAX_NUM_ROWS + 1][MAX_NUM_COLUMNS + 1];
} // End of constructor Tower
public class Position {
public int row;
public int column;
public Position(int row, int column) {
this.row = row;
this.column = column;
} // End of constructor Position
} // End of inner class Position
public void play() {
showIntro();
startGame();
} // End of method play
private void showIntro() {
System.out.println(" ".repeat(32) + "TOWERS");
System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.println("\n\n");
} // End of method showIntro
private void startGame() {
boolean diskMoved = false;
int column = 0;
int disk = 0;
int needle = 0;
int numDisks = 0;
int numErrors = 0;
int numMoves = 0;
int row = 0;
Step nextStep = Step.INITIALIZE;
String userResponse = "";
Position diskPosition = new Position(0, 0);
// Begin outer while loop
while (true) {
switch (nextStep) {
case INITIALIZE:
// Initialize error count
numErrors = 0;
// Initialize positions
for (row = 1; row <= MAX_NUM_ROWS; row++) {
for (column = 1; column <= MAX_NUM_COLUMNS; column++) {
positions[row][column] = 0;
}
}
// Display description
System.out.println("");
System.out.println("TOWERS OF HANOI PUZZLE.\n");
System.out.println("YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT");
System.out.println("TOWER, ONE AT A TIME, NEVER PUTTING A LARGER DISK ON A");
System.out.println("SMALLER DISK.\n");
nextStep = Step.SELECT_TOTAL_DISKS;
break;
case SELECT_TOTAL_DISKS:
while (numErrors <= 2) {
// Get user input
System.out.print("HOW MANY DISKS DO YOU WANT TO MOVE (" + MAX_NUM_ROWS + " IS MAX)? ");
numDisks = scan.nextInt();
System.out.println("");
numMoves = 0;
// Ensure the number of disks is valid
if ((numDisks < 1) || (numDisks > MAX_NUM_ROWS)) {
numErrors++;
// Handle user input errors
if (numErrors < 3) {
System.out.println("SORRY, BUT I CAN'T DO THAT JOB FOR YOU.");
}
}
else {
break; // Leave the while loop
}
}
// Too many user input errors
if (numErrors > 2) {
System.out.println("ALL RIGHT, WISE GUY, IF YOU CAN'T PLAY THE GAME RIGHT, I'LL");
System.out.println("JUST TAKE MY PUZZLE AND GO HOME. SO LONG.");
return;
}
// Display detailed instructions
System.out.println("IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE.");
System.out.println("3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,");
System.out.println("7 THE NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH");
System.out.println("2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15. WITH 3 DISKS");
System.out.println("THE CODE NAMES WOULD BE 11, 13 AND 15, ETC. THE NEEDLES");
System.out.println("ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL");
System.out.println("START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM");
System.out.println("TO NEEDLE 3.\n");
System.out.println("GOOD LUCK!\n");
disk = MAX_DISK_SIZE;
// Set disk starting positions
for (row = MAX_NUM_ROWS; row > (MAX_NUM_ROWS - numDisks); row--) {
positions[row][1] = disk;
disk = disk - 2;
}
printPositions();
nextStep = Step.SELECT_DISK_MOVE;
break;
case SELECT_DISK_MOVE:
System.out.print("WHICH DISK WOULD YOU LIKE TO MOVE? ");
numErrors = 0;
while (numErrors < 2) {
disk = scan.nextInt();
// Validate disk numbers
if ((disk - 3) * (disk - 5) * (disk - 7) * (disk - 9) * (disk - 11) * (disk - 13) * (disk - 15) == 0) {
// Check if disk exists
diskPosition = getDiskPosition(disk);
// Disk found
if ((diskPosition.row > 0) && (diskPosition.column > 0))
{
// Disk can be moved
if (isDiskMovable(disk, diskPosition.row, diskPosition.column) == true) {
break;
}
// Disk cannot be moved
else {
System.out.println("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE.");
System.out.print("WHICH DISK WOULD YOU LIKE TO MOVE? ");
}
}
// Mimic legacy handling of valid disk number but disk not found
else {
System.out.println("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE.");
System.out.print("WHICH DISK WOULD YOU LIKE TO MOVE? ");
numErrors = 0;
continue;
}
}
// Invalid disk number
else {
System.out.println("ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15.");
numErrors++;
if (numErrors > 1) {
break;
}
System.out.print("? ");
}
}
if (numErrors > 1) {
System.out.println("STOP WASTING MY TIME. GO BOTHER SOMEONE ELSE.");
return;
}
nextStep = Step.SELECT_NEEDLE;
break;
case SELECT_NEEDLE:
numErrors = 0;
while (true) {
System.out.print("PLACE DISK ON WHICH NEEDLE? ");
needle = scan.nextInt();
// Handle valid needle numbers
if ((needle - 1) * (needle - 2) * (needle - 3) == 0) {
// Ensure needle is safe for disk move
if (isNeedleSafe(needle, disk, row) == false) {
System.out.println("YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE,");
System.out.println("IT MIGHT CRUSH IT!");
System.out.print("NOW THEN, ");
nextStep = Step.SELECT_DISK_MOVE;
break;
}
diskPosition = getDiskPosition(disk);
// Attempt to move the disk on a non-empty needle
diskMoved = false;
for (row = 1; row <= MAX_NUM_ROWS; row++) {
if (positions[row][needle] != 0) {
row--;
positions[row][needle] = positions[diskPosition.row][diskPosition.column];
positions[diskPosition.row][diskPosition.column] = 0;
diskMoved = true;
break;
}
}
// Needle was empty, so move disk to the bottom
if (diskMoved == false) {
positions[MAX_NUM_ROWS][needle] = positions[diskPosition.row][diskPosition.column];
positions[diskPosition.row][diskPosition.column] = 0;
}
nextStep = Step.CHECK_SOLUTION;
break;
}
// Handle invalid needle numbers
else {
numErrors++;
if (numErrors > 1) {
System.out.println("I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN.");
System.out.println("BYE BYE, BIG SHOT.");
return;
}
else {
System.out.println("I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME. BUT WATCH IT,");
System.out.println("I ONLY ALLOW ONE MISTAKE.");
}
}
}
break;
case CHECK_SOLUTION:
printPositions();
numMoves++;
// Puzzle is solved
if (isPuzzleSolved() == true) {
// Check for optimal solution
if (numMoves == (Math.pow(2, numDisks) - 1)) {
System.out.println("");
System.out.println("CONGRATULATIONS!!\n");
}
System.out.println("YOU HAVE PERFORMED THE TASK IN " + numMoves + " MOVES.\n");
System.out.print("TRY AGAIN (YES OR NO)? ");
// Prompt for retries
while (true) {
userResponse = scan.next();
if (userResponse.toUpperCase().equals("YES")) {
nextStep = Step.INITIALIZE;
break;
}
else if (userResponse.toUpperCase().equals("NO")) {
System.out.println("");
System.out.println("THANKS FOR THE GAME!\n");
return;
}
else {
System.out.print("'YES' OR 'NO' PLEASE? ");
}
}
}
// Puzzle is not solved
else {
// Exceeded maximum number of moves
if (numMoves > MAX_NUM_MOVES) {
System.out.println("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN");
System.out.println("128 MOVES.");
return;
}
nextStep = Step.SELECT_DISK_MOVE;
break;
}
break;
default:
System.out.println("INVALID STEP");
break;
}
} // End outer while loop
} // End of method startGame
private boolean isPuzzleSolved() {
int column = 0;
int row = 0;
// Puzzle is solved if first 2 needles are empty
for (row = 1; row <= MAX_NUM_ROWS; row++) {
for (column = 1; column <= 2; column++) {
if (positions[row][column] != 0) {
return false;
}
}
}
return true;
} // End of method isPuzzleSolved
private Position getDiskPosition(int disk) {
int column = 0;
int row = 0;
Position pos = new Position(0, 0);
// Begin loop through all rows
for (row = 1; row <= MAX_NUM_ROWS; row++) {
// Begin loop through all columns
for (column = 1; column <= MAX_NUM_COLUMNS; column++) {
// Found the disk
if (positions[row][column] == disk) {
pos.row = row;
pos.column = column;
return pos;
}
} // End loop through all columns
} // End loop through all rows
return pos;
} // End of method getDiskPosition
private boolean isDiskMovable(int disk, int row, int column) {
int ii = 0; // Loop iterator
// Begin loop through all rows above disk
for (ii = row; ii >= 1; ii--) {
// Disk can be moved
if (positions[ii][column] == 0) {
continue;
}
// Disk cannot be moved
if (positions[ii][column] < disk) {
return false;
}
} // End loop through all rows above disk
return true;
} // End of method isDiskMovable
private boolean isNeedleSafe(int needle, int disk, int row) {
for (row = 1; row <= MAX_NUM_ROWS; row++) {
// Needle is not empty
if (positions[row][needle] != 0) {
// Disk crush condition
if (disk >= positions[row][needle]) {
return false;
}
}
}
return true;
} // End of method isNeedleSafe
private void printPositions() {
int column = 1;
int ii = 0; // Loop iterator
int numSpaces = 0;
int row = 1;
// Begin loop through all rows
for (row = 1; row <= MAX_NUM_ROWS; row++) {
numSpaces = 9;
// Begin loop through all columns
for (column = 1; column <= MAX_NUM_COLUMNS; column++) {
// No disk at the current position
if (positions[row][column] == 0) {
System.out.print(" ".repeat(numSpaces) + "*");
numSpaces = 20;
}
// Draw a disk at the current position
else {
System.out.print(" ".repeat(numSpaces - ((int) (positions[row][column] / 2))));
for (ii = 1; ii <= positions[row][column]; ii++) {
System.out.print("*");
}
numSpaces = 20 - ((int) (positions[row][column] / 2));
}
} // End loop through all columns
System.out.println("");
} // End loop through all rows
} // End of method printPositions
public static void main(String[] args) {
Tower tower = new Tower();
tower.play();
} // End of method main
} // End of class Tower

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>TOWER</title>
</head>
<body>
<pre id="output" style="font-size: 12pt;"></pre>
<script src="tower.js"></script>
</body>
</html>

View File

@@ -0,0 +1,254 @@
// TOWER
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
str += " ";
return str;
}
var ta = [];
// Print subroutine
function show_towers()
{
var z;
for (var k = 1; k <= 7; k++) {
z = 10;
str = "";
for (var j = 1; j <= 3; j++) {
if (ta[k][j] != 0) {
while (str.length < z - Math.floor(ta[k][j] / 2))
str += " ";
for (v = 1; v <= ta[k][j]; v++)
str += "*";
} else {
while (str.length < z)
str += " ";
str += "*";
}
z += 21;
}
print(str + "\n");
}
}
// Main control section
async function main()
{
print(tab(33) + "TOWERS\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
print("\n");
print("\n");
while (1) {
print("\n");
// Initialize
e = 0;
for (d = 1; d <= 7; d++) {
ta[d] = [];
for (n = 1; n <= 3; n++)
ta[d][n] = 0;
}
print("TOWERS OF HANOI PUZZLE.\n");
print("\n");
print("YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT\n");
print("TOWER, ONE AT A TIME, NEVER PUTTING A LARGER DISK ON A\n");
print("SMALLER DISK.\n");
print("\n");
while (1) {
print("HOW MANY DISKS DO YOU WANT TO MOVE (7 IS MAX)");
s = parseInt(await input());
print("\n");
m = 0;
if (s >= 1 && s <= 7)
break;
e++;
if (e < 2) {
print("SORRY, BUT I CAN'T DO THAT JOB FOR YOU.\n");
continue;
}
print("ALL RIGHT, WISE GUY, IF YOU CAN'T PLAY THE GAME RIGHT, I'LL\n");
print("JUST TAKE MY PUZZLE AND GO HOME. SO LONG.\n");
return;
}
// Store disks from smallest to largest
print("IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE.\n");
print("3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,\n");
print("7 THE NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH\n");
print("2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15. WITH 3 DISKS\n");
print("THE CODE NAMES WOULD BE 11, 13 AND 15, ETC. THE NEEDLES\n");
print("ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL\n");
print("START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM\n");
print("TO NEEDLE 3.\n");
print("\n");
print("GOOD LUCK!\n");
print("\n");
y = 7;
d = 15;
for (x = s; x >= 1; x--) {
ta[y][1] = d;
d -= 2;
y--;
}
show_towers();
while (1) {
print("WHICH DISK WOULD YOU LIKE TO MOVE");
e = 0;
while (1) {
d = parseInt(await input());
if (d % 2 == 0 || d < 3 || d > 15) {
print("ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15.\n");
e++;
if (e <= 1)
continue;
print("STOP WASTING MY TIME. GO BOTHER SOMEONE ELSE.\n");
return;
} else {
break;
}
}
// Check if requested disk is below another
for (r = 1; r <= 7; r++) {
for (c = 1; c <= 3; c++) {
if (ta[r][c] == d)
break;
}
if (c <= 3)
break;
}
for (q = r; q >= 1; q--) {
if (ta[q][c] != 0 && ta[q][c] < d)
break;
}
if (q >= 1) {
print("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE.\n");
continue;
}
e = 0;
while (1) {
print("PLACE DISK ON WHICH NEEDLE");
n = parseInt(await input());
if (n >= 1 && n <= 3)
break;
e++;
if (e <= 1) {
print("I'LL ASSUME YOU HIT THE WRONG KEY THI TIME. BUT WATCH IT,\n");
print("I ONLY ALLOW ONE MISTAKE.\n");
continue;
} else {
print("I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN.\n");
print("BYE BYE, BIG SHOT.\n");
return;
}
}
// Check if requested disk is below another
for (r = 1; r <= 7; r++) {
if (ta[r][n] != 0)
break;
}
if (r <= 7) {
// Check if disk to be placed on a larger one
if (d >= ta[r][n]) {
print("YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE,\n");
print("IT MIGHT CRUSH IT!\n");
print("NOW THEN, ");
continue;
}
}
// Move relocated disk
for (v = 1; v <= 7; v++) {
for (w = 1; w <= 3; w++) {
if (ta[v][w] == d)
break;
}
if (w <= 3)
break;
}
// Locate empty space on needle n
for (u = 1; u <= 7; u++) {
if (ta[u][n] != 0)
break;
}
ta[--u][n] = ta[v][w];
ta[v][w] = 0;
// Print out current status
show_towers();
// Check if done
m++;
for (r = 1; r <= 7; r++) {
for (c = 1; c <= 2; c++) {
if (ta[r][c] != 0)
break;
}
if (c <= 2)
break;
}
if (r > 7)
break;
if (m > 128) {
print("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN\n");
print("128 MOVES.\n");
return;
}
}
if (m == Math.pow(2, s) - 1) {
print("\n");
print("CONGRATULATIONS!!\n");
print("\n");
}
print("YOU HAVE PERFORMED THE TASK IN " + m + " MOVES.\n");
print("\n");
print("TRY AGAIN (YES OR NO)");
while (1) {
str = await input();
if (str == "YES" || str == "NO")
break;
print("\n");
print("'YES' OR 'NO' PLEASE");
}
if (str == "NO")
break;
}
print("\n");
print("THANKS FOR THE GAME!\n");
print("\n");
}
main();