Another freebasic miner

Game development specific discussions.
badidea
Posts: 1983
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Another freebasic miner

Postby badidea » Dec 26, 2019 22:59

Roland Chastain wrote:About the new FB Miner, I saw that you use the main window for the debug. If I were you, I would compile in console mode and write all the debug to the console. Just my two cents. ;)

Yes, I use that in-screen thing for debugging now, but it might also be useful for other text feedback to the player later. Maybe with a nicer font.

The 'logger' class can also log to file (if a filename is specified), then I can track the the messages with a simple 'tail -f' in a separate terminal. But I do most development I do my laptop without a second screen. Then open windows cover each other to quickly.
badidea
Posts: 1983
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Another freebasic miner

Postby badidea » Jan 05, 2020 0:38

I continued with the project again. Only a small addition for now: Ability to build ladders, using 'space' key.
https://nr100.home.xs4all.nl/badidea/mi ... -01-05.zip [640 KB]
Roland Chastain
Posts: 858
Joined: Nov 24, 2011 19:49
Location: France
Contact:

Re: Another freebasic miner

Postby Roland Chastain » Jan 07, 2020 13:31

Hi! Thank you for sharing. Successfully compiled and tested under Windows 64. Noticed a small thing: if you press the up arrow and in the same time the right arrow, the right arrow has no effect.

Good luck! I can't wait to see more. But even in the current state, it's already a beautiful program. :)
badidea
Posts: 1983
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Another freebasic miner

Postby badidea » Jan 07, 2020 17:47

Roland Chastain wrote:Noticed a small thing: if you press the up arrow and in the same time the right arrow, the right arrow has no effect.

That is correct. I can try to change it, but I fear that that code will be more complex.
badidea
Posts: 1983
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Another freebasic miner

Postby badidea » Jan 14, 2020 22:14

Not much changes to my game. I spend several days trying to figure out 'git'.
I think I understand the 'git workflow' now, although nothing fancy tried yet.
Tried 3 different free GUI's for 'git' at first: Very confusing experience. Then bask to basics: Via the command line.
Now I managed to integrate git via Ubuntu Mate's file manager 'caja', after several problems, I got it working:
https://nr100.home.xs4all.nl/badidea/miner/RabbitVCS_Ubuntu_Mate_2020-01-14.png
Current code on GitHub is probably not working now due to my tests.

Edit: inline image changed to link
Last edited by badidea on Feb 17, 2020 23:34, edited 1 time in total.
paul doe
Posts: 1175
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Another freebasic miner

Postby paul doe » Jan 15, 2020 4:02

For what is worth, I use GitHub Desktop to sync with my repository. Very simple and integrates seamlessly under Windows. You specify a local folder for the repository and it will sync it automatically with GitHub. Then, create a pull request, drag and drop files to the local folder and it automatically creates the diffs for you, which you can then commit. Very easy and painless ;)
BasicCoder2
Posts: 3519
Joined: Jan 01, 2009 7:03

Re: Another freebasic miner

Postby BasicCoder2 » Jan 15, 2020 9:12

@badidea,
Keep up the good work. I can't seem to get excited about programming at the moment but drop in occasionally to see what is happening here.
@Paul Doe,
I remember your 3d playground in your GitHub repository. Made me realise how far I was from being able to go beyond 2d graphics.
St_W
Posts: 1484
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: Another freebasic miner

Postby St_W » Jan 16, 2020 21:38

Can recommend the Git GUI https://www.syntevo.com/smartgit/ SmartGit for Linux (also available for Windows and Mac).
badidea
Posts: 1983
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Another freebasic miner

Postby badidea » Feb 17, 2020 23:42

Some update to code:
* Health bar and fall damage
* Wiggling flowers
* Rudimentary pick axe action
See GitHub link in first post.

I switched back to command line git commands, just as easy for now. And added a ssh-key 'trust'.
Roland Chastain
Posts: 858
Joined: Nov 24, 2011 19:49
Location: France
Contact:

Re: Another freebasic miner

Postby Roland Chastain » Feb 18, 2020 5:15

Very very nice! Congratulations.
badidea
Posts: 1983
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Another freebasic miner

Postby badidea » Feb 25, 2020 23:03

Code at GitHub updated. Visible things added:
* Pick axe and drill animation
* Block damage animation and removal
And various changes to the 'game engine'

Image
(for some weird reason the drill image was much larger then the pick axe image, I had to reduce it by 50% with nearest-neighbour, but still a bit to big)

At the same time, I try to figure out the trick to continue this (or any bigger project). Observations so far:
* Long breaks (> 1 week) are bad. During my Rebus project which took 2 weeks, I did not work on this miner game. When I continued, it was like some bright or stranger wrote the code. Very hard to continue then.
* Don't start with something big each day. You only stare at code then.
* Check the to-do list and start with something simple. Once you get into the 'flow' again, bigger hurdles can be taken.
* Keep a lot of notes and to-do items for your tomorrow-self. See previous point.
* Don't worry too much about the proper way to implement something. Start implementing, then do it better. Directly afterwards or put it on the to-do list.
badidea
Posts: 1983
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Another freebasic miner

Postby badidea » Feb 29, 2020 14:58

An essential part of miner game, are resources to mine. I have (images for) several resources: gold, iron, coal, etc.

I want to have the occurrence of certain minerals depend on depth (e.g iron close to the surface, and uranium at greater depth).
In addition, I want to cluster certain minerals, maybe in veins for a more realism.
Any suggestions / code for how to do this?

Illustration of simply randomly distributed resources:
Image
paul doe
Posts: 1175
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Another freebasic miner

Postby paul doe » Mar 01, 2020 11:21

badidea wrote:An essential part of miner game, are resources to mine. I have (images for) several resources: gold, iron, coal, etc.

I want to have the occurrence of certain minerals depend on depth (e.g iron close to the surface, and uranium at greater depth).
In addition, I want to cluster certain minerals, maybe in veins for a more realism.
Any suggestions / code for how to do this?

Well, there's almost infinite ways to do this. Here's a cellular automata method that will do some simplistic clustering:

Code: Select all

/'
  Represents a map of clustered states.
 
  Uses a very simple cellular automata to perform
  the clustering.
'/
type _
  ClusteredMap
 
  public:
    declare constructor( _
      byval as integer, _
      byval as integer, _
      byval as integer )
    declare destructor()
   
    declare property _
      width() as integer
    declare property _
      height() as integer
   
    declare function _
      cell( _
        byval as integer, _
        byval as integer ) _
      as integer
    declare sub _
      reset( _
        byval as single => 0.5 )
    declare sub _
      cluster( _
        byval as integer )
     
  private:
    declare constructor()
   
    declare function _
      rndBetween( _
        byval as integer, _
        byval as integer ) _
      as integer
    declare function _
      evalChance( _
        byval as single ) _
      as boolean
    declare function _
      countNeighborsFor( _
        byval as integer, _
        byval as integer, _
        byval as integer ) _
      as integer
   
    as integer _
      _map( any, any )
    as integer _
      _width, _
      _height, _
      _states
end type

constructor _
  ClusteredMap()
end constructor

constructor _
  ClusteredMap( _
    byval aWidth as integer, _
    byval aHeight as integer, _
    byval aStates as integer )
 
  _width => iif( aWidth < 1, 1, aWidth )
  _height => iif( aHeight < 1, 1, aHeight )
 
  redim _
    _map( 0 to _width - 1, 0 to _height - 1 )
 
  _states = aStates
end constructor

destructor _
  ClusteredMap()
end destructor

property _
  ClusteredMap.width() _
  as integer
 
  return( _width )
end property

property _
  ClusteredMap.height() _
  as integer
 
  return( _height )
end property

function _
  ClusteredMap.rndBetween( _
    byval a as integer, _
    byval b as integer ) _
  as integer
 
  return( rnd() * ( b - a ) + a )
end function

function _
  ClusteredMap.evalChance( _
    byval aChance as single ) _
  as boolean
 
  return( cbool( rnd() <= aChance ) )
end function

function _
  ClusteredMap.cell( _
    byval x as integer, _
    byval y as integer ) _
  as integer
 
  return( _map( x, y ) )
end function

function _
  ClusteredMap.countNeighborsFor( _
    byval aState as integer, _
    byval x as integer, _
    byval y as integer ) _
  as integer
 
  dim as integer _
    neighbors => 0
 
    for _
      dy as integer => -1 to 1
     
      for _
        dx as integer => -1 to 1
       
        if( _
          x + dx < 0 orElse _
          y + dy < 0 orElse _
          x + dx > _width - 1 orElse _
          y + dy > _height - 1 orElse _
          ( dx = 0 andAlso dy = 0 ) ) then
       
          continue for
        end if
       
        if( _map( x + dx, y + dy ) = aState ) then
          neighbors +=> 1
        end if
      next
    next
 
  return( neighbors )
end function

/'
  Seeds the map.
 
  The 'clutter' parameter controls how sparse the map will be.
  Higher values lead to more cluttered maps.
'/
sub _
  ClusteredMap.reset( _
    byval clutter as single => 0.5 )
 
  dim as single _
    s => _states / _height
 
  for _
    y as integer => 0 _
    to _height - 1
   
    for _
      x as integer => 0 _
      to _width - 1
     
      if( evalChance( clutter ) ) then
        _map( x, y ) => int( s * y ) + 1
      end if
    next
  next
end sub

/'
  Clusters the state map one iteration.
 
  The more you call this method, the more clustered the map
  will become. 'n' is the maximum distance to consider when
  clustering the states. The smaller it is, the smaller the
  clusters will be (more locality).
'/
sub _
  ClusteredMap.cluster( _
    byval n as integer )
 
  #define clamp( v, mn, mx ) _
    iif( v < mn, mn, iif( v > mx, mx, v ) )
 
  dim as integer _
    gain => 0
 
  for _
    y1 as integer = 0 _
    to _height - 1
 
  for _
    x1 as integer = 0 _
    to _width - 1   
 
      dim as integer _
        x2, y2
     
      do
        x2 = rndBetween( _
          clamp( x1 - n, 0, _width - 1 ), _
          clamp( x1 + n, 0, _width - 1 ) )
        y2 = rndBetween( _
          clamp( y1 - n, 0, _height - 1 ), _
          clamp( y1 + n, 0, _height - 1 ) )
      loop until( _
        x2 <> x1 andAlso _
        y2 <> y1 )
     
      dim as integer _
        aState => _map( x1, y1 ), _
        n1 => countNeighborsFor( aState, x1, y1 ), _
        n2 => countNeighborsFor( aState, x2, y2 )
     
      gain => n2 - n1
     
      if( gain > 0 ) then
        swap _
          _map( x1, y1 ), _
          _map( x2, y2 )
      end if
    next
  next
end sub

sub _
  drawMap( _
    byref aMap as ClusteredMap, _
    byval aSize as integer => 7 )
 
  dim as integer _
    halfSize => aSize \ 2
 
  for _
    y as integer => 0 _
    to aMap.height - 1
   
    for _
      x as integer => 0 _
      to aMap.width - 1
     
      dim as ulong _
        c
     
      select case as const( aMap.cell( x, y ) )
        '' Iron
        case 1
          c => rgba( 184, 114, 80, 255 )
       
        '' Uranium
        case 2
          c => rgba( 189, 206, 0, 255 )
      end select
     
      circle _
        ( halfSize + x * aSize, halfSize + y * aSize ), _
        halfSize, c, , , , f
    next
  next
end sub

randomize()

var _
  m => ClusteredMap( _
    64, 64, 2 )

dim as integer _
  iterations => 100, _
  cellSize => 7, _
  distance => 8

screenRes( _
  m.width * cellSize, _
  m.height * cellSize, _
  32 )
windowTitle( _
  "Togetherness CA (distance: " & distance )

m.reset( 0.3 )

drawMap( m, cellSize )

sleep()

for _
  i as integer => 1 _
  to iterations
 
  m.cluster( distance )
 
  screenLock()
    cls()
    drawMap( m )
  screenUnlock()
 
  sleep( 1, 1 )
next

sleep()

Image Image
Here, there are two resources represented by two different colors (iron and uranium; black would represent your 'default' terrain, say rock). As you can see, the 'distance' parameter controls the size of clusters. Smaller values lead to smaller clusters.
The zoning used when seeding the map is also very simplistic: it just distributes the amount of states evenly across the map's height. A different zoning algorithm will yield different looking maps. To implement the zoning in 'veins' as you suggest, you may try by perturbing a Worley noise map by a Perlin noise one, and then thresholding the result into the different resources you have. I can show you how to do that too if you want ;)
badidea
Posts: 1983
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Another freebasic miner

Postby badidea » Mar 01, 2020 12:09

Thanks, I did think of cellular automata yet. If I decide to go to an 'infinite' map (not sure yet), cellular automata is not optimal I think.

I was thinking about Perlin noise as well, in combination with a 1d-gaussian distribution for depth.

Another idea that came up was random walks with some direction preference to create veins.

But maybe I should study Lithostratigraphy a bit, for more realism.
paul doe
Posts: 1175
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: Another freebasic miner

Postby paul doe » Mar 01, 2020 14:50

badidea wrote:Thanks, I did think of cellular automata yet. If I decide to go to an 'infinite' map (not sure yet), cellular automata is not optimal I think.
...

Why not? Simply generate the map in chunks. Note that the noise functions used are deterministic, that is, they always return the same 'noise' value whenever queried with the same parameters. To seed them, just add a displacement. Of course, chunking the map is pretty easy if your miner can only go down (ala Namco's Mr. Driller). If you allow movement in any direction, you'll be in for a world of trouble (quite literally).
badidea wrote:...
I was thinking about Perlin noise as well, in combination with a 1d-gaussian distribution for depth.
...

Not really needed:

Code: Select all

/'
  -1 .. 1 range
'/
function _
  noise1( _
    byval x as integer ) _
  as single
 
  x => ( x shl 13 ) xor x
  return( ( 1.0! - ( ( x * ( x * x * 15731 + 789221) + 1376312589) and &h7fffffff) / 1073741824.0! ) )
end function

/'
  0 .. 1 range
'/
function _
  noise1n( _
    byval x as integer ) _
  as single
 
  return( ( noise1( x ) / 2.0! ) + 0.5! )
end function

/'
  -1 .. 1 range
'/
function _
  noise2( _
    byval x as integer, _
    byval y as integer ) _
  as single
 
  dim as integer _
    n => x + y * 57
    n => ( n shl 13 ) xor n
 
  return( ( 1.0! - ( ( n * ( n * n * 15731 + 789221 ) + 1376312589 ) and &h7fffffff ) / 1073741824.0! ) )   
end function

/'
  0 .. 1 range
'/
function _
  noise2n( _
    byval x as integer, _
    byval y as integer ) _
  as single
 
  return( ( noise2( x, y ) / 2.0! ) + 0.5! )
end function

/'
  Smooths the noise with bilinear interpolation.
 
  -1 .. 1 range
'/
function _
  smoothedNoise( _
    byval x as single, _
    byval y as single ) _
  as single
 
  dim as single _
    fractX => x - int( x ), _
    fractY => y - int( y )
 
  dim as integer _
    x1 => int( x ), _
    y1 => int( y ), _
    x2 => int( x - 1 ), _
    y2 => int( y - 1 )
 
  dim as single _
    value => _
      fractX * fractY * noise2( x1, y1 ) + _
      fractX * ( 1.0! - fractY ) * noise2( x1, y2 ) + _
      ( 1.0! - fractX ) * fractY * noise2( x2, y1 ) + _
      ( 1.0! - fractX ) * ( 1.0! - fractY ) * noise2( x2, y2 )
   
  return( value )
end function

/'
  Creates a turbulence texture (aka Perlin Noise).
 
  -1 .. 1 range
'/
function _
  turbulence( _
    byval x as single, _
    byval y as single, _
    byval size as single ) _
  as single
 
  dim as single _
    value => 0.0!, _
    initialSize => size
 
  do while( size >= 1.0! )
    value +=> smoothedNoise( x / size, y / size ) * size
    size /=> 2.0!
  loop
 
  value => value / initialSize
 
  return( iif( value < -1.0, -1.0, _
    iif( value > 1.0, 1.0, value ) ) )
end function

dim as integer _
  w => 256, _
  h => 256

screenRes( w, h, 32 )
windowTitle( "Noise playground" )

for _
  y as integer => 0 _
  to h - 1
 
  for _
    x as integer => 0 _
    to w - 1
   
    dim as single _
      n => turbulence( x, y, 32 )
   
    dim as ulong _
      c
   
    /'
      Remap the texture to colors
    '/
    if( n < 0.0! ) then
      dim as ubyte _
        p => 255 - abs( n ) * 255
     
      c => rgba( 0, 0, p, 255 )
    else
      dim as ubyte _
        p => n * 255
     
      c => rgba( 0, p, 0, 255 )
    end if
   
    pset _
      ( x, y ), _
      c
  next
next

sleep()

Image
All you need to do to add 'depth' is simply generate random numbers between -1 and 1, and interpret everything below 0 as depth. The above example shows this technique for a 2D map.

badidea wrote:...
Another idea that came up was random walks with some direction preference to create veins.
...

The random walk is certainly doable, but you'll find that it is very hard to coax it into something akin to veins (being essentially, well, random). One way you can achieve this is by weighting the direction chosen with a vector as you suggest. That way, it will still be random, but will tend to the direction you specify. However, this will be taken by the algorithm as a mere suggestion, since the variance of the distribution will still play itself out and you could wind up with a vein that goes clearly up even when you specified the direction to be down (if it just so happens that you get a distribution that's heavy on the tails).
badidea wrote:...
But maybe I should study Lithostratigraphy a bit, for more realism.

Isn't this a bit overkill for such a simple game? Here's another example, showing a simple way to achieve the same effect:

Code: Select all

/'
  -1 .. 1 range
'/
function _
  noise1( _
    byval x as integer ) _
  as single
 
  x => ( x shl 13 ) xor x
  return( ( 1.0! - ( ( x * ( x * x * 15731 + 789221) + 1376312589) and &h7fffffff) / 1073741824.0! ) )
end function

/'
  0 .. 1 range
'/
function _
  noise1n( _
    byval x as integer ) _
  as single
 
  return( ( noise1( x ) / 2.0! ) + 0.5! )
end function

/'
  -1 .. 1 range
'/
function _
  noise2( _
    byval x as integer, _
    byval y as integer ) _
  as single
 
  dim as integer _
    n => x + y * 57
    n => ( n shl 13 ) xor n
 
  return( ( 1.0! - ( ( n * ( n * n * 15731 + 789221 ) + 1376312589 ) and &h7fffffff ) / 1073741824.0! ) )   
end function

/'
  0 .. 1 range
'/
function _
  noise2n( _
    byval x as integer, _
    byval y as integer ) _
  as single
 
  return( ( noise2( x, y ) / 2.0! ) + 0.5! )
end function

/'
  Smooths the noise with bilinear interpolation.
 
  -1 .. 1 range
'/
function _
  smoothedNoise( _
    byval x as single, _
    byval y as single ) _
  as single
 
  dim as single _
    fractX => x - int( x ), _
    fractY => y - int( y )
 
  dim as integer _
    x1 => int( x ), _
    y1 => int( y ), _
    x2 => int( x - 1 ), _
    y2 => int( y - 1 )
 
  dim as single _
    value => _
      fractX * fractY * noise2( x1, y1 ) + _
      fractX * ( 1.0! - fractY ) * noise2( x1, y2 ) + _
      ( 1.0! - fractX ) * fractY * noise2( x2, y1 ) + _
      ( 1.0! - fractX ) * ( 1.0! - fractY ) * noise2( x2, y2 )
   
  return( value )
end function

/'
  Creates a turbulence texture (aka Perlin Noise).
 
  -1 .. 1 range
'/
function _
  turbulence( _
    byval x as single, _
    byval y as single, _
    byval size as single ) _
  as single
 
  dim as single _
    value => 0.0!, _
    initialSize => size
 
  do while( size >= 1.0! )
    value +=> smoothedNoise( x / size, y / size ) * size
    size /=> 2.0!
  loop
 
  value => value / initialSize
 
  return( iif( value < -1.0, -1.0, _
    iif( value > 1.0, 1.0, value ) ) )
end function

/'
  Creates a sinusoidal noise pattern.
 
  -1 .. 1 range
'/
function _
  sinNoise( _
    byval x as single, _
    byval y as single, _
    byval xPeriod as single, _
    byval yPeriod as single, _
    byval size as single, _
    byval turbSize as single, _
    byval turbPower as single ) _
  as single
 
  return( sin( _
    ( x * xPeriod / size + _
      y * yPeriod / size + _
      turbulence( x, y, turbSize ) * turbPower ) ) )
end function

dim as integer _
  w => 256, _
  h => 256

screenRes( w, h, 32 )
windowTitle( "Noise playground" )

for _
  y as integer => 0 _
  to h - 1
 
  for _
    x as integer => 0 _
    to w - 1
   
    dim as single _
      n => ( sinNoise( x, y, 2, 40, w, 16, 2.0 ) / 2.0! ) + 0.5!
   
    dim as ubyte _
      p => n * 255
   
    pset _
      ( x, y ), _
      rgba( p, p, p, 255 )
  next
next

sleep()

Image
Play around with the parameters to see if they're useful somehow.

Return to “Game Dev”

Who is online

Users browsing this forum: No registered users and 2 guests