Post your code!

User avatar
Stix
Member
 
Posts: 1379
Joined: Fri Aug 04, 2017 14:19
Location: USA
IRC: nil
In-game: Stix [+alts]

Re: Post your code!

by Stix » Sun Jan 13, 2019 23:13

I edited my previous program to fix some bugs and colorize the health status information.

Example:
High-health = colorize green.
Medium-health = colorize yellow.
Low-health = colorize red.

And here's the updated source-code:
Code: Select all
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define RED   "\x1B[31m"
#define GRN   "\x1B[32m"
#define YEL   "\x1B[33m"
#define RESET "\x1B[0m"

int hunger;
int food;
int thirst;
int water;
int medkit;
int day;
int choice;
int amount;
int turn;
int status;
int status1;
int condition;
int item_value;
int situation;
int health;
int energy;
int attack;
int recover;
int enemy_health;
int enemy_energy;
int enemy_attack;
int enemy_recover;
char death[100];
char item[100];
char situation1[100];
char choice1[100];
char choice2[100];
char choice3[100];
char choice4[100];

int defaults() //stores defaults values that are called at th beginning of the program
{
  status1 = 0;
  status = 0;
  day = 0;
  health = 10;
  hunger = 0;
  thirst = 0;
  food = 5;
  water = 3;
  medkit = 1;
}

int fight_defaults() //stores default values for the functions fight()
{
  enemy_health = 10;
  enemy_energy = 4;
  enemy_attack = 3;
  enemy_recover = 2;
 
  energy = 4;
  attack = 3;
  recover = 2;
}

int drink()
{
  if(water < 1)
    printf("\nYou have no more water to drink.\n");
  else
    if(thirst > 0)
      {
   thirst -= 5;
   water--;
      }
  if(thirst < 0)
    thirst = 0;
}

int eat()
{
  if(food < 1)
    printf("\nYou have no more food to eat.\n");
  else
    if(hunger > 0)
      {
   hunger -= 3;
   food--;
      }
  if(hunger < 0)
    hunger = 0;
}

int do_nothing() //a function that does nothing (hack)
{
  status = status;
}

int heal()
{
  if(medkit < 1)
    printf("\nYou don't have any medkits.\n");
  else 
    if(health < 10)
      {
   printf("\nYou healed.\n");
   health += 3;
   medkit--;
      }
    else
      printf("\nYou are already at full health.\n");
}

int health_bar()
{
  if(condition == 0)
    {
      if(health <= 3)
   printf(RED "\nHealth: %d out of 10"RESET, health);
      if(health > 3)
   {
     if(health < 7)
       printf(YEL "\nHealth: %d out of 10"RESET, health);
   }
      if(health > 6)
   printf(GRN "\nHealth: %d out of 10"RESET, health);
    }
     
  if(condition == 1)
    {
      if(health <= 3)
   printf(RED "\n Health: %d" RESET, health);
      if(health > 3)
   {
     if(health < 7)
       printf(YEL "\n Health: %d" RESET, health);
   }
      if(health > 6)
   printf(GRN "\n Health: %d" RESET, health);
    }
 
  if(condition == 2)
    {
      if(enemy_health <= 3)
   printf(RED "           Health: %d" RESET, enemy_health);
      if(enemy_health > 3)
   {
     if(enemy_health < 7)
       printf(YEL "           Health: %d" RESET, enemy_health);
   }
      if(enemy_health > 6)
   printf(GRN "           Health: %d" RESET, enemy_health);
    }
}
     
int choices() //a function that gives a set of choices based on the current situation
{
  int r2 = rand() % 3;
  int r3 = rand() % 5;
  int r4 = rand() % 2;
 
  if(situation == 1)
    {
      if(choice == 1)
   drink();
      if(choice == 2)
   eat();
      if(choice == 3)
   do_nothing;
      if(choice == 4)
   heal();
      if(choice == 5)
   status = 1;
    }
 
  if(situation == 2)
    {
      switch(choice)
   {
   case 1:
     printf("\nYou started a fight!\n");
     fight();
     break;
   case 2:       
     if(r2 == 2)
       {
         printf("\nYou got attacked by a stranger!\n");
         fight();
       }
     if(r2 <= 1)
       {
         printf("\nYou ran away from a fight.\n");
         do_nothing();
       }
     break;
   case 3:      
     if(r4 == 0)
       break;
     if(r4 == 1)
       printf("\nYou got attacked by a stranger!\n");
     fight();
     break;
   case 4:
     do_nothing();
     break;
   case 5:
     status = 1;
     break;
   }
    }
 
  if(situation == 3)
    {         
      if(r3 == 0)
   {
     strcpy(item, "food-item");
     item_value = 1;
   }
      if(r3 == 1)
   {
     strcpy(item, "water-bottle");
     item_value = 2;
   }
      if(r3 == 2)
   {
     strcpy(item, "medkit");
     item_value = 3;
   } 
      if(r3 >= 3)
   printf("\nThe box was empty!\n");
               
      amount = r3;
   
      if(r3 <= 2)
   {   
     if(choice == 1)
       {
         printf("\nYou found %d %s(s).\n", amount, item);
        
         if(item_value == 1)
      food += amount;
         if(item_value == 2)
      water += amount;
         if(item_value == 3)
      medkit += amount;
       }
   }
     
      if(choice == 2)
   do_nothing();
      if(choice == 3)
   do_nothing();
      if(choice == 4)
   do_nothing();
      if(choice == 5)
   status = 1;
    }
}


int situations() //a function that randomly picks a situation to be current
{
  int r = rand() % 5; //grabbing a random number (0-4)

  if(r <= 2)
    {
      strcpy(situation1, "You are walking through an abandoned town."); //setting situation
      strcpy(choice1, "Drink."); //setting choices for the situation
      strcpy(choice2, "Eat.");
      strcpy(choice3, "Do nothing.");
      strcpy(choice4, "Heal.");
      situation = 1;
    }
 
  if(r == 3)
    {
      strcpy(situation1, "While walking you meet a stranger."); //setting situation
      strcpy(choice1, "Fight."); //setting choices for the situation
      strcpy(choice2, "Run.");
      strcpy(choice3, "Do nothing.");
      strcpy(choice4, " ");
      situation = 2;
    }

  if(r == 4)
    {
      strcpy(situation1, "While walking you find a box."); //setting situation
      strcpy(choice1, "Open it."); //setting choices for the situation
      strcpy(choice2, "Do nothing.");
      strcpy(choice3, " ");
      strcpy(choice4, " ");
      situation = 3;
    }
}

int fight() //a function that handles fighting between the player and enemy ai
{
  fight_defaults();
 
  while(1) //loop repeats until a 'break' is called
    {
      printf("\n>>>>>>>STATS<<<<<<<");
      printf("  >>>>>>>ENEMY<<<<<<<");
      printf("\n*******************");
      printf("  *******************");
      condition = 1;
      health_bar();
      condition = 2;
      health_bar();
      condition = 0;
      printf("\n Energy: %d        ", energy);
      printf("   Energy: %d         ", enemy_energy); 
      printf("\n Attack: %d        ", attack);   
      printf("   Attack: %d         ", enemy_attack);
      printf("\n Recover: %d       ", recover);
      printf("   Recover: %d        ", enemy_recover);
      printf("\n*******************");
      printf("  *******************\n");
     
      turn = 0; //sets turn to players beginning of every loop
     
      if(turn == 0) //takes input if conditions are met
   {
     printf("\nYour turn!\n");
    
     while(turn == 0)
       {
         printf("\n1) Fight.\n2) Recover.\n3) Skip turn.\n");
         scanf("%d", &choice);
        
         if(choice == 1) //calculates the input and makes decisions based on it     
      {
        if(energy >= 1)
          {
            enemy_health += enemy_recover - attack; //attacking enemy
            energy -= 1;
            break; //breaking the loop
          }
        else
          printf("\nYou dont have enough energy to attack!\n");
        continue; //restarting the loop
      }
        
         if(choice == 2)
      energy += recover; //recover energy
         break;
         if(choice == 3)
      do_nothing(); //the if statement executes nothing (hack)
         break;           
       }                      
   }
 
      if(enemy_health < 1) //checking if the player won the fight
   {
     printf("\nYou won the fight!\n");
     break;
   }
     
      death_event();

      if(status1 == 1)
   break;
     
      turn = 1; //sets turn to enemy() function end of every loop
      enemy();
    }
}

int enemy() //a function that takes a number (0-2) and makes decisions based on the result
{
  if(turn == 1)
    {
      printf("\nEnemy's turn!");
      int r = rand() % 3; //grabbing a number (0-2)
     
      switch(r)
   {
   case 0:
     /*fall through (which makes the enemy more likely to attack)*/
   case 1:   
     if(enemy_energy >= 1)
       {
         health += recover - enemy_attack; //attacking player
         enemy_energy -= 1;
         printf("\n\nThe enemy attacked!\n");
         break;
       }
     /*fall through (so the enemy has to recover if they're unable to attack)*/
   case 2:   
     enemy_energy += enemy_recover; //recovering energy
     printf("\n\nThe enemy recovered energy.\n");
     break;
   }
    }


int death_event() //Computes cause of death and prints the death-message accordingly.
{
  if(hunger > 10) //checks if the player has starved
    {
      status = 3;
      strcpy(death, "hunger");
    }
  if(thirst > 10) //checks if the player is dehydrated
    {
      status = 3;
      strcpy(death, "thirst");
    }
  if(health < 1) //checks if the player was killed by the enemy ai
    {
      status = 3;
      strcpy(death, "a stranger");
    }
  if(status == 3)
    {
      /*notifying the user of their characters death*/
      printf("\nDeath by %s, you survived %d days. ", death, day);
      status = 1;
      printf("Press \'1\' to restart or \'2\' to quit: ");
      scanf("%d", &choice);
     
      if(choice == 1)
   {
     status = 2;
     status1 = 1;
   }
     
      if(choice == 2)
   {
     status = 1;
     status1 = 1;
   }
     
      if(choice > 2)
   {    
     /*informing the user of an invalid number for input*/
     printf("\nError: to high a value.\n\n");
     status = 1;
   }
    }
}


int win() //checks if the user wins and notifies them accordingly
{
  if(day > 29)
    printf("\nCongratulations, you survived all %d days!\n\n", day);
}


main()
{
 
  defaults(); //grabbing default values
 
  for(day = 1; day < 31; day++) //a loop that will run until day == 30
    {
      situations(); //executing a function that randomly sets the situation
     
      printf("\nDay %d: %s\n", day, situation1);
      printf("\n>>>>>>>>>>>>>>>>>>STATS<<<<<<<<<<<<<<<<<<");
      printf("\n*****************************************");
      health_bar();
      printf("\nHunger: %d out of 10", hunger);
      printf("\nThirst: %d out of 10", thirst);
      printf("\n\nYou have %d water-bottle(s).", water);
      printf("\nYou have %d food-item(s).", food);
      printf("\nYou have %d medkit(s).", medkit);
      printf("\n*****************************************\n");
      printf("\nShould you:\n1) %s\n2) %s\n3) %s\n4) %s\n", choice1, choice2, choice3, choice4);
     
      scanf("%d", &choice);
     
      /*executing a function that will make decisions based on the current situation and
        the users input*/
      choices();

      if(situation == 1) //incrementing hunger/thirst every loop if the condition is met
   {
     hunger++;
     thirst+=2;
   }

      if(status == 1)
   break;
      if(status == 2)
   defaults(); //grabbing default values
      continue;

      death_event(); //executing a function that checks if the player died
      win(); //executing a function that will check if the user won
     
      if(status == 1)
   break;
      if(status == 2)
   defaults(); //grabbing default values
      continue;
    }

  return(0);
}
Hey, what can i say? I'm the bad guy.
Next release of your favorite game? "Bugfixes" --Wuzzy
[OLD]The new Minetest-offtopic can be found here: https://minetestoftopic.createaforum.com/index.php
 

User avatar
sorcerykid
Member
 
Posts: 1130
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Mon Jan 14, 2019 11:57

I have a habit of going through code and finding astounding amounts of extraneous logic. When it comes to senseless redundancy in programming, I'll admit I'm very anal retentive. Nearly half the mods used on JT2 have been refactored or rewritten from scratch, ultimately culling hundreds of lines. Here's a section of code from Zorman3000's pathfinder API that I recently cleaned up:

I managed to reduce this:
Code: Select all
  local start_x_sign = (start_pos.x - end_pos.x) / math.abs(start_pos.x - end_pos.x)
  local start_z_sign = (start_pos.z - end_pos.z) / math.abs(start_pos.z - end_pos.z)
  local end_x_sign = (end_pos.x - start_pos.x) / math.abs(end_pos.x - start_pos.x)
  local end_z_sign = (end_pos.z - start_pos.z) / math.abs(end_pos.z - start_pos.z)

  -- Correct the signs if they are nan
  if math.abs(start_pos.x - end_pos.x) == 0 then
    start_x_sign = -1
    end_x_sign = 1
  end
  if math.abs(start_pos.z - end_pos.z) == 0 then
    start_z_sign = -1
    end_z_sign = 1
  end


down to only this:
Code: Select all
local base_x_sign = base.pos.x <= goal_pos.x and -1 or 1
local goal_x_sign = -base_x_sign
local base_z_sign = base_pos.z <= goal_pos.z and -1 or 1
local goal_z_sign = -base_z_sign
 

User avatar
Hybrid Dog
Member
 
Posts: 2721
Joined: Thu Nov 01, 2012 12:46

Re: Post your code!

by Hybrid Dog » Sat Jan 19, 2019 14:19

I've written code for nearly spherical lighting.
Minetest default:
Image
Spherical:
Image
I didn't make a 3d version and haven't implemented in minetest yet.
https://github.com/minetest/minetest/is ... -446650534
I don't know the performance impact. It uses vectors instead of scalars for light spreading.
Code: Select all
#include <stdlib.h> // malloc, EXIT_*
#include <string.h> // memset
#include <math.h>
#include <png.h>

#define EXIT_PNG(F) if (!F) { \
   fprintf(stderr, "%s\n", bild.message); \
   return EXIT_FAILURE; \
}

#define CLAMP(V, A, B) (V) < (A) ? (A) : (V) > (B) ? (B) : (V)

#define u8 unsigned char

struct pixel {
   u8 r;
   u8 g;
   u8 b;
   u8 a;
};
#define PIXELBYTES 4

struct matrix {
   int w;
   int h;
   float *data;
};

struct image {
   int w;
   int h;
   struct pixel *pixels;
};

/*! \brief get y, cb and cr values each in [0;1] from u8 r, g and b values
 *
 * there's gamma correction,
 * see http://www.ericbrasseur.org/gamma.html?i=1#Assume_a_gamma_of_2.2
 * 0.5 is added to cb and cr to have them in [0;1]
 */
static void rgb2ycbcr(u8 or, u8 og, u8 ob, float *y, float *cb, float *cr)
{
   float divider = 1.0f / 255.0f;
   float r = powf(or * divider, 2.2f);
   float g = powf(og * divider, 2.2f);
   float b = powf(ob * divider, 2.2f);
   *y = (0.299f * r + 0.587f * g + 0.114f * b);
   *cb = (-0.168736f * r - 0.331264f * g + 0.5f * b) + 0.5f;
   *cr = (0.5f * r - 0.418688f * g - 0.081312f * b) + 0.5f;
}

/*! \brief the inverse of the function above
 *
 * numbers from http://www.equasys.de/colorconversion.html
 * if values are too big or small, they're clamped
 */
static void ycbcr2rgb(float y, float cb, float cr, u8 *r, u8 *g, u8 *b)
{
   float vr = (y + 1.402f * (cr - 0.5f));
   float vg = (y - 0.344136f * (cb - 0.5f) - 0.714136f * (cr - 0.5f));
   float vb = (y + 1.772f * (cb - 0.5f));
   float exponent = 1.0f / 2.2f;
   vr = powf(vr, exponent);
   vg = powf(vg, exponent);
   vb = powf(vb, exponent);
   *r = CLAMP(vr * 255.0f, 0, 255);
   *g = CLAMP(vg * 255.0f, 0, 255);
   *b = CLAMP(vb * 255.0f, 0, 255);
}

/*! \brief Convert an rgba image to 4 ycbcr matrices with values in [0, 1]
 */
static struct matrix *image_to_matrices(struct image *bild)
{
   int w = bild->w;
   int h = bild->h;
   struct matrix *matrices = malloc(
      PIXELBYTES * sizeof(struct matrix));
   for (int i = 0; i < PIXELBYTES; ++i) {
      matrices[i].w = w;
      matrices[i].h = h;
      matrices[i].data = malloc(w * h * sizeof(float));
   }
   for (int i = 0; i < w * h; ++i) {
      struct pixel px = bild->pixels[i];
      // put y, cb, cr and transpatency into the matrices
      rgb2ycbcr(px.r, px.g, px.b,
         &matrices[0].data[i], &matrices[1].data[i], &matrices[2].data[i]);
      float divider = 1.0f / 255.0f;
      matrices[3].data[i] = px.a * divider;
   }
   return matrices;
}

/*! \brief Convert 4 matrices to an rgba image
 *
 * Note that matrices becomes freed.
 */
static struct image *matrices_to_image(struct matrix *matrices)
{
   struct image *bild = malloc(sizeof(struct image));
   int w = matrices[0].w;
   int h = matrices[0].h;
   bild->w = w;
   bild->h = h;
   struct pixel *pixels = malloc(w * h * PIXELBYTES);
   for (int i = 0; i < w * h; ++i) {
      struct pixel *px = &pixels[i];
      ycbcr2rgb(matrices[0].data[i], matrices[1].data[i], matrices[2].data[i],
         &px->r, &px->g, &px->b);
      //~ float a = matrices[3].data[i] * 255;
      //~ px->a = CLAMP(a, 0, 255);
      px->a = 255;
   }
   for (int i = 0; i < PIXELBYTES; ++i) {
      free(matrices[i].data);
   }
   free(matrices);
   bild->pixels = pixels;
   return bild;
}

/*! \brief Here my algorithm resides
 *
 * \param mat The 4 matrices obtained form image_to_matrices.
 * \param s The input parameter.
 */
static void apply_algorithm(struct matrix *mat, int s)
{
   // preparation
   int w = mat->w; // input width
   int h = mat->h;
   float *input = mat->data;
   int w2 = w; // output width
   int h2 = h;
   int input_size = w * h * sizeof(float);
   int output_size = input_size;
   float *d = malloc(output_size);

////////////////////////////////////////////////////////////////////////////////

   // y x offset values:
   // 1 -1, 1 1, -1 1, -1 -1
#define YOFF(V) (1 - ((V) & 2))
#define XOFF(V, YO) ((YO) * ((((V) << 1) & 2) - 1))

   struct light {
      float vals[4];
   };

   // Initialize light map
   int lm_w = w+2;
   int lm_h = h+2;
   struct light lightmap[(lm_h)*lm_w + lm_w];
   memset(lightmap, 0, lm_w * lm_h * sizeof(struct light));

   // Initialize blockage map
   u8 blocked[(lm_h)*lm_w + lm_w];
   memset(lightmap, 0, lm_w * lm_h);
   for (int y = 1; y < h+1; ++y) {
      for (int x = 1; x < w+1; ++x) {
         // Light is blocked where the input image is bright enough
         blocked[y*lm_w + x] = input[y*w + x] > 0.5f;
      }
   }


   // Add start light source in the middle
   struct light source = {{15.0, 15.0, 15.0, 15.0}};
   lightmap[(lm_h/2)*lm_w + lm_w/2] = source;

   // Spread light until nothing changes
   const float sqrt2 = sqrt(2.0);
   int spread_cnt = 5318008;
   while (spread_cnt > 0) {
      spread_cnt = 0;
      for (int y = 1; y < h+1; ++y) {
         for (int x = 1; x < w+1; ++x) {
            struct light *l = &lightmap[y*lm_w + x];
            for (int v = 0; v < 4; ++v) {
               float light = l->vals[v];
               if (light == 0)
                  // No light for this direction
                  continue;

               int y_off = YOFF(v);
               int x_off = XOFF(v, y_off);
               int spread_before = spread_cnt;

               // Propagate horizontally and vertically or the other way
               // round
               float light_neigh = light - 1.0f;
               // Get the vector pointing 90° left to where v points to
               int v_left = (v + 3) % 4;
               int y_off_left = YOFF(v_left);
               int x_off_left = XOFF(v_left, y_off_left);
               // Get the middle between where v and v_left points to,
               // this is the horizontal or vertical neighbour left to
               // the diagonal neighbour from before
               y_off_left = (y_off_left + y_off) / 2;
               x_off_left = (x_off_left + x_off) / 2;
               int i_left = (y+y_off_left)*lm_w + x+x_off_left;
               if (!blocked[i_left]
                     && light_neigh > lightmap[i_left].vals[v]) {
                  // Update the light for the same vector but for the
                  // horizontal or vertical neighbour
                  lightmap[i_left].vals[v] = light_neigh;
                  ++spread_cnt;
               }
               // Analogous for the right one
               int v_right = (v + 1) % 4;
               int y_off_right = YOFF(v_right);
               int x_off_right = XOFF(v_right, y_off_right);
               y_off_right = (y_off_right + y_off) / 2;
               x_off_right = (x_off_right + x_off) / 2;
               int i_right = (y+y_off_right)*lm_w + x+x_off_right;
               if (!blocked[i_right]
                     && light_neigh > lightmap[i_right].vals[v]) {
                  // Update the light for the same vector but for the
                  // horizontal or vertical neighbour
                  lightmap[i_right].vals[v] = light_neigh;
                  ++spread_cnt;
               }

               // Propagate diagonally
               // Do not propagate if left and right were blocked
               int i_diag = (y+y_off)*lm_w + x+x_off;
               float light_diag = light - sqrt2;
               if (!blocked[i_diag] && spread_before < spread_cnt
                     && light_diag > lightmap[i_diag].vals[v]) {
                  // Update the light for the same vector but for the
                  // diagonal neighbour
                  lightmap[i_diag].vals[v] = light_diag;
                  ++spread_cnt;
               }
            }
         }
      }
      fprintf(stderr, "%d lights spread.\n", spread_cnt);
   }

   // Move the light to the picture

   int i = 0;
   for (int y = 0; y < h; ++y) {
      for (int x = 0; x < w; ++x) {
         float light = 0.0;
         struct light *l = &lightmap[(y+1)*lm_w + x+1];
         for (int v = 0; v < 4; ++v) {
            if (light < l->vals[v])
               light = l->vals[v];
         }
         int light_quantized = light + 0.5f;
         d[i++] = light_quantized / 15.0f;
      }
   }

////////////////////////////////////////////////////////////////////////////////

   // update the matrix
   mat->data = d;
   mat->w = w2;
   mat->h = h2;

   // tidy up
   free(input);
}

/*! \brief applies the algorithm on each matrix
 *
 * \param bild The image, it's content is changed when finished.
 * \param s Must be a natural number.
 */
void do_an_image(struct image **bild, int s)
{
   struct matrix *matrices = image_to_matrices(*bild);
   //~ for (int i = 0; i < PIXELBYTES; ++i) {
   for (int i = 0; i < 1; ++i) {
      apply_algorithm(&(matrices[i]), s);
   }
   *bild = matrices_to_image(matrices);
}

int main(int argc, char **args)
{
   if (argc != 2) {
      fprintf(stderr, "Missing arguments, usage: <cmdname> "
         "<input>\n");
      return EXIT_FAILURE;
   }
   int input = atoi(args[1]);
   if (input < 2) {
      fprintf(stderr, "Invalid: %d\n",
         input);
      return EXIT_FAILURE;
   }

   png_image bild;
   memset(&bild, 0, sizeof(bild));
   bild.version = PNG_IMAGE_VERSION;
   EXIT_PNG(png_image_begin_read_from_stdio(&bild, stdin))

   int w = bild.width;
   int h = bild.height;
   bild.flags = PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB;
   bild.format = PNG_FORMAT_RGBA;
   struct pixel *pixels = malloc(w * h * 4);
   EXIT_PNG(png_image_finish_read(&bild, NULL, pixels, 0, NULL))


   struct image origpic = {w = w, h = h, pixels = pixels};
   struct image *newpic = &origpic;
   do_an_image(&newpic, input);
   bild.width = newpic->w;
   bild.height = newpic->h;
   free(pixels);
   pixels = newpic->pixels;
   free(newpic);


   EXIT_PNG(png_image_write_to_stdio(&bild, stdout, 0, pixels, 0, NULL));
   free(pixels); // redundant free to feed valgrind
   return EXIT_SUCCESS;
}

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪
 

User avatar
sorcerykid
Member
 
Posts: 1130
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Sat Jan 19, 2019 16:30

Nice work HybridDog, I presume lighting calculations are handled server side?

Here's a quick one-liner tcsh alias for creating file backups. The "bak" command will maintain a sequential file history with permissions and timestamps in tact. This can come in useful when editing config files or testing lua scripts, without the need for a full fledged git repository.

Usage is very straightforward:

    % bak minetest 3 conf
In this case "minetest" is the base file name, 3 is the next number in the sequence, and "conf" is the extension. The file named "minetest.conf" will be thus copied using option -ip to "minetest.old3.conf" in the same directory. You will be given a warning if the sequence number already exists, so there's no worry about accidental clobbering.

Just add the following two lines to ~/.tcshrc, and you're ready to go!

Code: Select all
alias bak 'cp -ip \!:1.\!:3 \!:1.bak\!:2.\!:3'
alias old 'cp -ip \!:1.\!:3 \!:1.old\!:2.\!:3'


Of course, with some simple tweaks to the alias, it would also be possible to archive the backups into a subdirectory such as "_old" or even to tarball them. And if you want to get really fancy, you could automate the file numbering as follows:

Code: Select all
set DOLLAR='$'
alias bak '/bin/ls -1v \!:1.bak*.\!:2 | tail -n 1 | awk -F"." "{printf substr(${DOLLAR}2,4)+1}" | xargs -0 -ISEQ cp -ip \!:1.\!:2 \!:1.bakSEQ.\!:2'


Now it's as simple as typing bak minetest conf, to backup the minetest configuration file.

Note that this requires creating an environment variable $DOLLAR, due to the quirky rules about interpolation within double quoted strings.
 

User avatar
joe7575
Member
 
Posts: 328
Joined: Mon Apr 24, 2017 20:38
Location: Germany, in the deep south
GitHub: joe7575
In-game: JoSto

Re: Post your code!

by joe7575 » Sun Jan 20, 2019 11:26

Generate a HTML file with table of contents from 'lua_api.txt':
* install markdown2
* download 'lua_api.txt'
* run the script
Code: Select all
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

# first install markdown2 via: pip install markdown2

import markdown2

header = u"""<!doctype html>
    <html>
        <head>
            <title>Lua Modding API Reference - Minetest Modding Book</title>
            <meta charset="UTF-8">
            <meta name="author" content="rubenwardy">
            <style>
                code { background-color:#F0F0F0;}
            </style>
        </head>
    <body>
        <h2>Table of Contents</h2>
""".encode("utf-8")

footer = """
    </body>
</html>""".encode("utf-8")

html = markdown2.markdown_path("./lua_api.txt", extras=["toc"])
toc = html.toc_html.encode("utf-8")
body = html.encode("utf-8")

file("./lua_api.html", "wt").write(header + toc + body + footer)
print "Ready."
Sent from my Commodore 64. My Mods: TechPack, Hyperloop, Tower Crane, Lumberjack, Iron Age, Entrance, Minecart, Signs Bot.
 

User avatar
sorcerykid
Member
 
Posts: 1130
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Mon May 27, 2019 04:11

Here's a string library function that supports conditional pattern matching with capture groups stored to the underscore variable.

Code: Select all
string.is_match = function( text, glob )
     -- use underscore variable to preserve captures
     _ = { string.match( text, glob ) )
     return #_ > 0
end


This is a simple example of how string.is_match() can be used:

Code: Select all
if string.is_match( input, "^load (%w+)$" ) or string.is_match( input, "^load (%w+%.txt)$" ) then
     print( "loading file " .. _[ 1 ] )
     -- code to load file
elseif string.is_match( input, "^save (%w+)$" ) or string.is_match( input, "^save (%w+%.txt)$" ) then
     print( "saving file " .. _[ 1 ] )
     -- code to save file
end
 

User avatar
Gael de Sailly
Member
 
Posts: 736
Joined: Sun Jan 26, 2014 17:01
Location: Lyon, France
GitHub: Gael-de-Sailly
IRC: Gael-de-Sailly
In-game: Gael-de-Sailly Ginkgoo

Re: Post your code!

by Gael de Sailly » Wed May 29, 2019 01:05

Tiny code snippet for CSV file parsing – if anyone need it.
Code: Select all
local function read_csv(path)
   local file = io.open(path, "r")
   local t = {}
   for line in file:lines() do
      if line:sub(1,1) ~= "#" and line:find("[^%,% ]") then
         table.insert(t, line:split(",", true))
      end
   end
   return t
end


Larger piece of code to load binary numbers, including floats, that will be needed for the next version of geo-mapgen.
Code: Select all
numberio = {}

-- Define number types

local ntypes = {
   int8    = {1, false, true},
   uint8   = {1, false, false},
   int16   = {2, false, true},
   uint16  = {2, false, false},
   float16 = {2, true, 5, 10},
   int32   = {4, false, true},
   uint32  = {4, false, false},
   float32 = {4, true, 8, 23},
   int64   = {8, false, true},
   uint64  = {8, false, false},
   float64 = {8, true, 11, 52},
}

for name, ntype in pairs(ntypes) do
   numberio[name] = ntype
end

-- Endianness

local bigendian = false

function numberio.get_endianness()
   return bigendian
end

function numberio.set_endianness(is_bigendian)
   bigendian = is_bigendian
end

local function reverse_table(t)
   local n = #t
   for i=1, math.floor(n/2) do
      t[i], t[n+1-i] = t[n+1-i], t[i]
   end
end

-- READ NUMBERS

local function read_bits(bytes, ...)
   local pattern = {...}
   if not bigendian then
      reverse_table(bytes)
   end
   local r = 8
   local b = table.remove(bytes, 1)
   local v = {}
   for _, nbits in ipairs(pattern) do
      local n = 0
      while nbits > r do
         nbits = nbits - r
         n = n + b*2^nbits
         r = 8
         b = table.remove(bytes, 1)
      end
      if nbits > 0 then
         local d = 2^(r-nbits)
         n = n + math.floor(b/d)
         b = b % d
         r = r - nbits
      end
      table.insert(v, n)
   end

   return unpack(v)
end

local function get_reader(ntype)
   local len, is_float, p1, p2 = unpack(ntype)

   local reader
   if is_float then -- Float
      local exp_offset = -2^(p1-1)+1
      local exp_max = 2^p1-1
      local denormal_factor = 2^(exp_offset+1)
      local mantissa_factor = 2^-p2
      reader = function(input)
         local sign, exp, mantissa = read_bits(input, 1, p1, p2)
         sign = (-1)^sign
         if exp == 0 then
            if mantissa == 0 then
               return sign * 0
            end
            return sign * denormal_factor * mantissa * mantissa_factor
         elseif exp == exp_max then
            if mantissa == 0 then
               return sign*math.huge
            else
               return sign < 0 and math.sqrt(-1) or -math.sqrt(-1)
            end
         end
         return sign * 2^(exp+exp_offset) * (1 + mantissa*mantissa_factor)
      end
   elseif p1 then -- Signed int
      local nbits = len*8
      local max = 2^(nbits-1)
      local decr = 2^nbits
      reader = function(input)
         local n = read_bits(input, nbits)
         if n >= max then
            return n - decr
         end
         return n
      end
   else -- Unsigned int
      local nbits = len*8
      reader = function(input)
         return read_bits(input, nbits)
      end
   end
   ntype.reader = reader
   return reader
end

function numberio.readnumber(input, ntype, pack)
   if type(ntype) == "string" then
      ntype = ntypes[ntype]
   end
   local reader = ntype.reader
   if not reader then
      reader = get_reader(ntype)
   end

   local len = ntype[1]
   local inputlen = math.floor(#input/len)

   if inputlen == 1 then
      local bytes = {input:byte(1, len)}
      if pack then
         return {reader(bytes)}
      else
         return reader(bytes)
      end
   else
      local v = {}
      local start = 1
      for i=1, inputlen do
         local stop = i*len
         local bytes = {input:byte(start, stop)}
         table.insert(v, reader(bytes))
         start = stop + 1
      end

      if pack then
         return v
      else
         return unpack(v)
      end
   end
end
Just realize how bored we would be if the world was perfect.
 

User avatar
sorcerykid
Member
 
Posts: 1130
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Fri Dec 27, 2019 22:42

Seasons greetings everyone!

Here is a handy Lua command line script for listing all nodes that are owned by specific players (like steel doors, locked chests, etc.) in your map database. Just change the following variables at the head of the script:

  • path - the path of the map.sqlite database to be read
  • area - the region of the map to be searched, specified as minimum and maximum mapblock positions
  • owner_name- the name of the owner to search for (nil will search for all owners)
  • node_name- the name of the owned nodes to search for (nil will search for all owned nodes)
Keep in mind, this script can take some time to process very large databases, so it is strongly recommended to limit the search area to no more than ~50 cubic mapblocks at a time.

Code: Select all
require( "maplib" )

local path = "/home/minetest/.minetest/worlds/world/map.sqlite"
local area = MapArea( { x = -25, y = -10, z = -25 }, { x = 25, y = 5, z = 25 } )
local node_name = "protector:protect"
local node_owner = "sorcerykid"

local map_db = MapDatabase( path, false )
print( "Creating cache..." )
map_db.create_cache( false )
print( "Examining database..." )

local function to_node_pos( idx, mapblock_idx )
        local mapblock_pos = mapblock_idx and decode_pos( mapblock_idx ) or 0
        local pos = { }

        pos.x = ( idx % 16 ) + mapblock_pos.x * 16
        idx = math.floor( idx /16 )
        pos.y = ( idx % 16 ) + mapblock_pos.y * 16
        idx = math.floor( idx /16 )
        pos.z = ( idx % 16 ) + mapblock_pos.z * 16

        return pos
end

local block_total = 0
local node_count = 0

for index, block in map_db.iterate_area( area ) do
        local nodemeta_list = block.get_nodemeta_list( )
        local nodename_map = block.nodename_map
        local node_list

        -- don't waste cpu cycles getting nodes unless there is meta
        if next( nodemeta_list ) then
                node_list = block.get_node_list( )
        end

        block_total = block_total + 1

        for i, m in pairs( nodemeta_list ) do
                local name = nodename_map[ node_list[ i + 1 ].id ]
                local owner = m.fields.owner

                if owner and ( not node_owner or owner == node_owner ) and ( not node_name or name == node_name ) then
                        node_count = node_count + 1

                        print( string.format( "%s owned by %s at (%s).",
                                name, owner, pos_to_string( to_node_pos( i, index ) )
                        ) )
                end
        end
end

print( string.format( "Found %d owned nodes (scanned %d map blocks).", node_count, block_total ) )
 

User avatar
sorcerykid
Member
 
Posts: 1130
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Tue Jan 07, 2020 02:30

Here is a helper function for concatenating the elements of a table that is comprised of subtables (or any other non-string value) by means of a join function.

str = table.join( list, sep, func )
Returns a string by concatenating the return values of a join function

  • list - the table to be concatenated
  • sep - the delimiter to separate the return values of the join function
  • func - the function to be executed for each element of the table, receiving the index and value as arguments and returning a string
Note that just like Lua's builtin table.concat( ) function, only indexed arrays are supported, not key-value stores.

Code: Select all
function table.join( list, sep, func )
        local str = ""
        for i, v in ipairs( list ) do
                local res = func( i, v )
                if res ~= nil then
                        str = i > 1 and str .. sep .. res or str .. res
                end
        end
        return str
end


This is particularly useful when you want to list all the values from a specific field in a database. Instead of iterating over every record and manually appending the delimeters, the entire operation can be performed in a single statement. For example, assuming that you have a database of players, each with fields "name" and "rank".

Code: Select all
-- OLD METHOD:
local str = ""
for i, v in ipairs( users ) do
     if i > 0 then
          str = str .. ","
     end
     str = str .. v.name .. "(" .. v.rank .. ")"
end

-- NEW METHOD
local str = table.join( users, ",", function ( i, v )
     return v.name .. "(" .. v.rank .. ")"
end )


Where this function truly excels, however, is when dynamically building formspec strings (as with the dropdown[] and textlist[] elements), since it mitigates the need to break apart a series of string concatenations simply to perform a loop.

Code: Select all
-- BEFORE
formspec = formspec
     .. "label[0.5,0.5;The following " .. #results .. " results were found:]"
     .. "textlist[0.5,1.0;3.5,4.0;results;"

     for i, v in pairs( results ) do
          formspec = ( i > 1 and formspec .. "," or formspec ) .. minetest.formspec_escape( v.label )
     end

     formspec = formspec .. string.format( ";%d;false]", result_idx )

-- AFTER
formspec = formspec
     .. "label[0.5,0.5;The following " .. #results .. " results were found:]"
     .. "textlist[0.5,1.0;3.5,4.0;results;"
     .. table.join( results, ",", function ( i, v ) return minetest.formspec_escape( v.label ) end )
     .. string.format( ";%d;false]", result_idx )
 

User avatar
sorcerykid
Member
 
Posts: 1130
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Thu Jan 09, 2020 02:06

Some more helper functions that are used in a lot of my mods since they can greatly simplify conditionals and assignments.

node = minetest.get_node_above( pos, off )
Returns the node directly above the position or at a specified vertical offset from the position (negative for below).

  • pos - the reference node position
  • off - the vertical offset from the position, defaults to 1

nval = math.clamp( val, min, max )
Returns the numeric value clamped to the lower and upper boundaries.

  • val - the value to be clamped
  • min - the lower boundary to clamp to
  • max - the upper boundary to clamp to
npos = vector.offset( pos, x, y, z )
Returns a vector offset along three axes from the starting vector. This is a less verbose alternative to vector.add( ) in which a vector must be supplied as the second argument.

  • pos - the starting vector position
  • x - the offset along the x-axis, or 0 for no offset
  • y - the offset along the y-axis, or 0 for no offset
  • z - the offset along the z-axis, or 0 for no offset
npos = vector.offset_y( pos, y )

Returns a vector offset along the y-axis from the starting vector. This is a convenient shorthand for vector.offset( ) with zero offset for both horizontal axes.

  • pos - the starting vector position
  • y - the offset along the y-axis, defaults to 1
Simply add the following code to builtin/common/misc_helpers.lua to make these functions available to all of your games:

Code: Select all
minetest.get_node_above = function ( pos, off )
        return minetest.get_node( { x = pos.x, y = pos.y + ( off or 1 ), z = pos.z } )
end

vector.offset = function ( pos, x, y, z )
        return { x = pos.x + x, y = pos.y + y, z = pos.z + z }
end

vector.offset_y = function ( pos, y )
        return { x = pos.x, y = pos.y + ( y or 1 ), z = pos.z }
end

math.clamp = function ( val, min, max )
        return val < min and min or val > max and max or val
end
Last edited by sorcerykid on Thu Jan 09, 2020 12:38, edited 1 time in total.
 

User avatar
orwell
Member
 
Posts: 801
Joined: Wed Jun 24, 2015 18:45
Location: Raxacoricofallapatorius
GitHub: orwell96
IRC: orwell96_mt
In-game: orwell

Re: Post your code!

by orwell » Thu Jan 09, 2020 07:51

Hey, you didn't post the actual code...
Lua is great!
List of my mods
I like singing. I like dancing. I like ... niyummm...
 

User avatar
Hybrid Dog
Member
 
Posts: 2721
Joined: Thu Nov 01, 2012 12:46

Re: Post your code!

by Hybrid Dog » Fri Jan 10, 2020 21:28

orwell wrote:Hey, you didn't post the actual code...

It is there now (probably the post was edited).



There is code and documentation for a Priority Queue and more: https://github.com/minetest/minetest/pull/7683/files
Testing has shown that a Binary Heap is faster than the Pairing Heap in practice in lua(jit).

‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪‮
‮‪
 

User avatar
sorcerykid
Member
 
Posts: 1130
Joined: Fri Aug 26, 2016 15:36
Location: Illinois, USA
GitHub: sorcerykid
In-game: Nemo

Re: Post your code!

by sorcerykid » Tue Jan 14, 2020 01:21

I created this handy function for debugging, but it can serve many other purposes for examining numeric values in Lua.

res = bits_format( str, ... )
Extends the string.format() function with an additional %b format specifier for outputting numbers in binary notation. The str is a string that consists of one or more format specifiers, each associated with an argument for substitution.

    %[flag][field-width][:group-width]b
The %b format specifier accepts a flag of either 0 to pad the field with leading zeroes or - to left-justify the field. With no flag, the field will be padded with leading spaces instead. If no field width is specified then the only the minimum digits necessary to represent the number in binary notation will be shown, with no leading spaces or zeros. If the field is not wide enough to accommodate the entire number in binary notation, then it will be extended (no digits will be truncated). Sets of bits may also be evenly spaced provided an optional group width.

Code: Select all
local function bits_format( str, ... )
        local args = { ... }

        local function parse_token( num, attr1, attr2, attr3 )
                if not tonumber( num ) or num < 0 then error( "Invalid or missing argument, expected number." ) end

                local len = math.min( tonumber( attr2 ) or 0, 56 ) or 0 -- limit to 56-bits
                local has_pad = attr1 == "0"
                local has_rev = attr1 == "-"
                local div = tonumber( attr3 ) or 0
                local res = ""

                local idx = 0
                while num > 0 or idx < len do
                        local rem = num % 2

                        -- prepend digits, since working from least to most significant bit
                        if div > 0 and idx % div == 0 then
                                res = " " .. res                                -- insert space between sets
                        end
                        if rem == 1 then
                                res = "1" .. res                                -- insert digit 1
                        elseif has_pad or num > 0 then
                                res = "0" .. res                                -- else, insert digit 0 or insert zero padding
                        else
                                res = has_rev and res .. " " or " " .. res      -- else, insert space padding
                        end

                        num = ( num - rem ) / 2
                        idx = idx + 1
                end

                return res
        end

        str = string.gsub( str, "(%%(.-)([A-Za-z]))", function ( token, attrs, option )
                if option == "b" and string.is_match( attrs, "^([0-]?)([0-9]*)$" ) or string.is_match( attrs, "^([0-]?)([0-9]*):([0-9]+)$" ) then
                        return parse_token( remove( args, 1 ), _[ 1 ], _[ 2 ], _[ 3 ] )
                else
                        return string.format( token, remove( args, 1 ) )
                end
        end )

        return str
end


Here are three examples of different inputs and the results, including pass-through string.format() specifiers:

Code: Select all
SOURCE
local num1 = 0xFF00
local num2 = 512
local num3 = 65000

print( bitwise.format( "The number %d in binary is %b.", num1, num1 ) )
print( bitwise.format( "dec = %d, bin = %016b, hex = %04x", num2, num2, num2 ) )
print( bitwise.format( "%032:4b", num3 ) )

OUTPUT
The number 65280 in binary is 1111111100000000.
dec = 512, bin = 0000001000000000, hex = 0200
0000 0000 0000 0000 1111 1101 1110 1000
 

Previous

Return to General Discussion



Who is online

Users browsing this forum: No registered users and 1 guest