Wednesday, February 24, 2021

cool storybook cookbook

This page of storybook HOWTO/recipes lets us do a trick in storybook... we were already using a set up in preview to style components, but we wanted to be able to include 

<Canvas>

  <Story name='Some specific argument' args={{ children: 'Button' }}>

without that Story showing up in the sidebar. (Detailed request from our PM)

Tuesday, February 23, 2021

two pieces on keeping it simple

I am not a fan of frameworks in general. 

Here's a piece on The web didn't change; you did and reminding folks that for personal projects, nothing is keeping you from PHP/HTML and Javascript. 

Even for more professional settings, there's this piece on The Case Against Web Frameworks. Browsers can do so much today, and ECMAScript/JS has so much great syntax built in.

I've been using PHPwebhosting for over a decade now (and a similar site before that.) It's such a great investment, a site I can ssh into and put up whatever fool PHP I want! 

I worry about the disconnect between the tools I use "for fun" and the stuff I use for work - one of my secret sauce advantages over the years was using the same kind of stacks for both - but it is what it is. And maybe I'll slowly convince people at work to keep it simple as well...

saul bass in the place, saul bass kicks some... butt

Monday, February 22, 2021

half a billion to learn that UI matters

Citibank just got a $500 million lesson in the importance of UI design.

Actually maybe that bubbles up to UX as well; besides a crap UI, there was a lot of complexity in understanding account type and what not.

Saturday, February 20, 2021

du sort human-readable diskusage

 Something I remember looking up before a few times:

du -hs * | sort -h

(via this serverfault question)

That gets disk usage 'h'uman readable (i.e. Gb, Mb, K at different scales),  's'umarizes (so you don't get the detailed listing for every subdirectory) and then sort has a parallel 'h'uman flag to deal with the readable values.

Saturday, February 13, 2021

how to draw and manage a hex grid on a computer

One of my favorite board games is Hey, That's My Fish! It's a really cute game where color-coded teams of penguins slide around a hexagon-based ice field. Each piece of ice has 1 2 or 3 fish, and every time a penguin exits a piece the penguin claims the fish, but the piece melts, so the board is ever changing, and there's a lot of strategy in trying to block other penguins with melted ice and penguin bodies. 

It's a strategy game, but not TOO think-y, or too much secret information to keep track of, which is a good balance for me.

There's an excellent version for iOS (it's ideal as an iPad party game) and Android, but also a free online version at yucata.de. The latter is at a really nice and congenial site (the Germans love their board games) but the interface is a little clunky for Zoom/video conference play... it's a bit geared at remote players taking turns at their leisure, and the whole site is a bit Web 1.5 feeling and requires accounts to be set up, so I'm not sure if I feel comfortable recommending it for casual work get togethers.

(From a UX standpoint, one really nice feature at the Yucata version is it uses colors to highlight the last move each player made... great when a game is going fairly slowly or people aren't being 100% attentive)

I'm not sure if it's worth coding my own version of the game (like I did for the NSFW card game Joking Hazard ) especially since it only goes up to 4 players. (If it went up to 6 or 8 it would be perfect for work game night...) But, the idea of teaching a computer to do a hex grid was interesting! Many strategy games I grew up with used hexes, (I especially remember Steve Jackson's OGRE)... there's more symmetry in the 6 directions (vs square grids, where diagonal moves go a lot further than the cardinal directions)x

As my friend Jon pointed out, a hex grid can kind of be represented as a plain old square grid, with every other row offset, but still since each hex has 6 neighbors and each square grid has 8, it was worth sketching things out and trying to get a sense of how things flowed when you treated a hex grid in terms of x,y coordinates...

I do love Apple Notes App for this kind of thing!

Interesting - every hex has East/West and North/South neighbors, but when coerced to a square grid, like... G has neighbors to the NE and SE (namely C and M) but M, one row down, has NW and SW. Every other row, then, acts a bit differently (which is why it seemed like mod arithmetic might do the trick, as we'll see later on.

I don't have super-strong spatial intuition, so I decided to just start coding incrementally rather than planning everything out. And I decided to code things up in P5, using its convenient editor. 

My first program just drew a hex grid. To be frank I just dabbled numbers until things looked right.
I started using a convention of "i,j" for the hex location, and "x,y" for the coordinate on screen. Empirically I found out if "BASESZ" was the base width... every other row had to be offset, hence

x = (i + (j % 2) / 2) * BASESZ 

and the y was packed a little tighter, based on how I was drawing hexes:

(I didn't bother to check if these were "perfect" hexes, frankly if they're a little squashed it's fine)

For my second program, I started using a new data structure, just a flat list of all hexes... not quite object oriented per se, but each hex knew where it was located and could draw itself. I also draw a circle inside each hex... it was easier to do a simple pythagorean dist() to see if the mouse was over the thing than any fancier math. On every mouse move, I reset each hex's "on" key to false, and then for each hex if it's in the distance it gets set to true.

Hex Experiment 3 finally gets a little interesting. Penguins in "Hey, That's My Fish!" move a bit like a queen or rook in chess, as far as they want in any of the 6 directions (but can be blocked by missing hexes or other penguins) so I thought I'd show what moves were available from whatever hex the mouse was on:

Besides the previous version's flat list, I stored each hex in an [i,j] two dimensional array, (It's a little weird when rolling your own 2D array, Rows come first, then Columns... at least the way I think about it) I then made 6 functions, each one returning an array of hexes leading out in that direction - basically a walker loop start at the base hex and proceeding whatever direction. East and West were easy, but NW/NE/SW/SE needed a little finagling... to look up the neighbor in the row above it was moving leftor right, like for southwest the change in i (for horizontal position changed)

i -=((j+1)%2);

where j is the current row. By making it visual, it was pretty quick to figure out what the math should be - a crude but effective way of getting it right.

Finally Hex Experiment 4 uses a slightly better syntax, letting each directional be a call to the same function with just a "incrementI, incrementJ" pair of functions. That way if I ever do make penguins, I only have to fix the loop to get blocked by other penguins or missing hexes in one function instead of 6.

For safety, here's the final code:


const BASESZ = 40;

const COLCOUNT = 8;
const ROWCOUNT = 8;

const hexList = [];
const hexGrid = [];

const SHIFTLEFT = 50;
const SHIFTDOWN = 100;

const OFF = 0;
const ON = 1;
const OVER = 2;

function setup() {
  createCanvas(400, 400);

  for (let j = 0; j < ROWCOUNT; j++) {
    hexGrid[j] = [];
    for (let i = 0; i < COLCOUNT; i++) {
      const hex = makeHexForLoc(i, j);
      hexList.push(hex);
      hexGrid[j][i] = hex;
    }
  }
}

function draw() {
  background(220);
  push();
  translate(SHIFTLEFT, SHIFTDOWN);
  hexList.map((hex) => {
    drawHex(hex)
  });
  pop();
  noLoop();
}

function makeHexForLoc(i, j) {
  const x = (i + (j % 2) / 2) * BASESZ;
  const y = (BASESZ * 0.75) * j;
  return {
    i,
    j,
    x,
    y,
    mode: OFF
  };
}



function mouseMoved() {
  hexList.map((hex) => {
    hex.mode = OFF;
  });

  const boardX = mouseX - SHIFTLEFT;
  const boardY = mouseY - SHIFTDOWN;

  let pickedHex = null;
  hexList.map((hex) => {
    if (dist(boardX, boardY, hex.x, hex.y) < BASESZ / 2) {
      pickedHex = hex;
    }
  });
  if (pickedHex) {
    //Check Easts
    getOpenHexesInDir(pickedHex,()=>1,()=>0)
    	.map(hex => hex.mode = ON);
    //Check Wests
    getOpenHexesInDir(pickedHex,()=>-1,()=>0)
    	.map(hex => hex.mode = ON);
    //Check North Easts    
    getOpenHexesInDir(pickedHex,(i,j)=>j%2,()=>-1)
    	.map(hex => hex.mode = ON);
    //Check North Wests    
    getOpenHexesInDir(pickedHex,(i,j)=>-(j+1)%2,()=>-1)
    	.map(hex => hex.mode = ON);
    //Check South Easts
    getOpenHexesInDir(pickedHex,(i,j)=>j%2,()=>1)
    	.map(hex => hex.mode = ON);
    //Check South Wests    
    getOpenHexesInDir(pickedHex,(i,j)=>-(j+1)%2,()=>1)
    	.map(hex => hex.mode = ON);
    pickedHex.mode = OVER;
  }
  loop();
}


function getOpenHexesInDir(hex,iTransform,jTransform){
  let {
    i,
    j
  } = hex;
   const res = [];
  while (i < COLCOUNT && j < ROWCOUNT && 
  	i >= 0 && j >= 0) {
    res.push(hexGrid[j][i]);
    i += iTransform(i,j);
    j += jTransform(i,j);
  }
  return res;
}

function drawHex(hex) {
  const {
    x,
    y,
    mode
  } = hex;
  const sz = BASESZ / 2;
  push();
  translate(x, y);
  //sides
  line(-sz, -sz / 2, -sz, sz / 2);
  line(sz, -sz / 2, sz, sz / 2);

  //top
  line(-sz, -sz / 2, 0, -sz);
  line(0, -sz, sz, -sz / 2);

  //bottom
  line(-sz, sz / 2, 0, sz);
  line(0, sz, sz, sz / 2);

  fill(255);
  if (mode === OVER) fill(255, 128, 128);
  if (mode === ON) fill(128, 128, 255);
  circle(0, 0, sz * 1.5);

  pop();
}

I'm not sure if I'll ever make the online version of this game, but this was a satisfying bit of computer math to get done on a Saturday morning.

Friday, February 12, 2021

sneaking in a background image on an arbitrary storybook standalone doc

To misquote an old musical:

I'm just a dev who can't say no,
I'm in a terrible fix,
I always say 'C'mon, let's go!'
when I just oughta say 'Nix!'

Anyway. A designer wondered if we could put in a background image on a specific standalone doc. Here's the typescript object I came up with:

export type StorybookStyleInjectorProps = {
  property: string;
  value: string;
};

export const StorybookStyleInjector = ({
  property,
  value
}: StorybookStyleInjectorProps) => {
  const elems = Array.prototype.slice.call( 
  	document.getElementsByClassName('sbdocs-wrapper') );
  setTimeout(() => {
    for(const elem of elems) {
      elem.style[property] = value;
    }
  }, 0);
  return null;
};

export default StorybookStyleInjector;
I had to use setTimeout for unclear reasons regarding rendering... 

Sample usage:

<StorybookStyleInjector
  property='backgroundImage'
  value='url(/images/introduction/bg-pattern.svg)'
/>
Lets see if my PR gets approved....

Thursday, February 11, 2021

full webpage PDFs from iPhone

iPhone life hack. When you make a screenshot in Safari, if you click on the thumbnail before it flies off to "Photos", the edit screen has a "Full Page" option that lets you save a PDF of the whole long page instead of just a PNG of what you see. (via McGST, great blog)

Saturday, February 6, 2021

online board games for team game nights or fun with other friends

Board game fans! The giant Quarantine Games doc - a bunch of games that can be played online, can be overwhelming. A while back some friends and I started a more modest vouched for games-list.

In some ways I'm only a so-so board game fan, especially strategic ones - like, taking an hour or something just to find out at the end that I'm not as smart as my friends has limited appeal. I do love a good drawing game though - Jack Box Bundles of course has many (Tee KO being my favorite) but a drawasaurus is a very good Pictionary clone.

Last night I played Ticket to Ride on Steam - just found out that there's a Tabletop2gether Humble Bundle on Steam, with all the variations of Ticket to Ride and some other solid titles for a name-your-price supporting St. Jude Children's. (>= $10 unlocks it all)

Finally, I found out of my favorite charming and strategic but fast and easy games "Hey, That's My Fish!" is available at Yucata.de - slightly klutzy interface and it's just one game among many, but the implementation of Fish seems pretty solid! (Not as polished as the awesome iPad version, which also has a one player mode, but good enough)

Wednesday, February 3, 2021

how fast is fast

 At work, talking about using PageSpeed Insights as a tool for checking page performance. Sort of happy that my sites (kirk.isalienbill.com) score so high! Of course they don't do ALL that much....

And of course "lighthouse" is the other popular tool.

I'm surprised how low so many sites, including some companies I worked for who were very concerned about such things. score on these things.