Accounts e-mail HP

Trend charts generator

Can you help improve your favourite game? Hardcore C mages, talented artists, and players with any level of experience are welcome!

Trend charts generator

Postby Davide » Tue Apr 02, 2013 1:01 am

A post-game charts generator.
This program produces outputs like this.

The script takes in input a Freeciv scorelog file, and outputs an HTML page containing a set of trend charts. Requires:
  • jqPlot library installed on the webserver;
  • Freeciv 2.5, or 2.3 with this patch.

Code: Select all
#!/bin/bash
# requires freeciv >= 2.5.0

# _Postulate:_ data structures are contiguous, never 'unset' in the middle.

scorelog=$1; pageout=$2

if [ ! -e "$scorelog" ]; then
    echo "Scorelog file: $scorelog does not exist" >&2
    exit 1
fi

# title and subtitle are rendered in html (' must be escaped by \)

sym_tag[0]=bnp
sym_active[0]=true
sym_button[0]="Trade"
sym_title[0]="Trade"
sym_subtitle[0]="Overall cities trade, minus corruption"
sym_vlabel[0]="cities trade [trade units]"

sym_tag[1]=taxout
sym_active[1]=true
sym_button[1]="Taxes"
sym_title[1]="Taxes"
sym_subtitle[1]="State Treasury income ♦ Gold per turn"
sym_vlabel[1]="income [gold]"

sym_tag[2]=gold
sym_active[2]=true
sym_button[2]="Gold"
sym_title[2]="Gold"
sym_subtitle[2]="Amount of State Treasury"
sym_vlabel[2]="treasury [gold]"

sym_tag[3]=techout
sym_active[3]=true
sym_button[3]="Science"
sym_title[3]="Science"
sym_subtitle[3]="Scientific expenses • Bulbs per turn"
sym_vlabel[3]="expenses [bulbs]"

sym_tag[4]=rates
sym_active[4]=false
sym_button[4]="Rates"
sym_title[4]="Rates"
sym_subtitle[4]="Tax, Science, Luxury partitioning rates"
sym_vlabel[4]="///"

sym_tag[5]=mfg
sym_active[5]=true
sym_button[5]="Production"
sym_title[5]="Production"
sym_subtitle[5]="Manufacturing power • Shields per turn"
sym_vlabel[5]="production [shields]"

sym_tag[6]=cunits
sym_active[6]=true
sym_button[6]="Civilians"
sym_title[6]="Civilian Units"
sym_subtitle[6]="Amount of civilian units under control"
sym_vlabel[6]="units"

sym_tag[7]=munits
sym_active[7]=true
sym_button[7]="Army"
sym_title[7]="Military Units"
sym_subtitle[7]="Amount of military units under control"
sym_vlabel[7]="units"

sym_tag[8]=techs
sym_active[8]=true
sym_button[8]="Techs"
sym_title[8]="Technologies"
sym_subtitle[8]="Amount of technologies discovered"
sym_vlabel[8]="technologies"

sym_tag[9]=gov
sym_active[9]=false
sym_button[9]="Govenrment"
sym_title[9]="Governments"
sym_subtitle[9]="Ruling government"
sym_vlabel[9]="///"

sym_tag[10]=wonders
sym_active[10]=true
sym_button[10]="Wonders"
sym_title[10]="Wonders"
sym_subtitle[10]="Wonders of the World owned"
sym_vlabel[10]="number of wonders"

# read scorelog file
declare -Ag chart
while :; do
    read -r line && lastline=false || lastline=true
    re='^ *([^ ]+) *(.*)$'; [[ "$line" =~ $re ]]
    cmd=${BASH_REMATCH[1]}; data=${BASH_REMATCH[2]};

    case "$cmd" in
    tag)
   re='^ *([^ ]+) ([^ ]+) *$'; [[ "$data" =~ $re ]]
        i=-1; while i=$((i + 1)); do [ "${ass_tag_id[$i]}" ] || break; done
   ass_tag_id[$i]=${BASH_REMATCH[1]}; ass_tag_name[$i]=${BASH_REMATCH[2]}
   ;;
    turn)
   re='^ *([^ ]+).*$'; [[ "$data" =~ $re ]]
   totturns=${BASH_REMATCH[1]} # after fetch completes, contains tot turns
        ;;
    addplayer)
   re='^ *([^ ]+) ([^ ]+) [^,]+, *([^,]+).*$'; [[ "$data" =~ $re ]]
   i=-1
   while i=$((i + 1)); do
       if [ "${ass_plr_id[$i]}" = "${BASH_REMATCH[2]}" ] ||
          [ ! "${ass_plr_id[$i]}" ]
       then break
       fi
   done
        ass_plr_id[$i]=${BASH_REMATCH[2]}
   ass_plr_name[$i]=${BASH_REMATCH[3]}
   ass_plr_jointurn[$i]=${BASH_REMATCH[1]}
        ;;
    delplayer)
   if ! :; then  # delplayer is buggy in 2.3.2. See gna.org/bugs/?20069
        re='^ *([^ ]+) ([^ ]+) *$'; [[ "$data" =~ $re ]]
   tmp_turn=${BASH_REMATCH[1]}; tmp_id=${BASH_REMATCH[2]}
   i=-1
   while i=$((i + 1)); [ "${ass_plr_id[$i]+x}" ]; do
         # if delplayer refers to a player added in same turn, delplayer
         # was wrongly written after addplayer in the scorelog; in such
         # case, the new player has already been recorded over the old.
       if [ "${ass_plr_id[$i]}" = "$tmp_id" ] &&
               [ "${ass_plr_jointurn[$i]})" != $tmp_turn ];
       then
      ass_plr_jointurn[$i]='' ass_plr_id[$i]='' ass_plr_name[$i]=''
       fi
   done
   fi
        ;;
    data)
        re='^ *([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) *$'; [[ "$data" =~ $re ]]
   tmp_1=${BASH_REMATCH[1]}; tmp_2=${BASH_REMATCH[2]}
   tmp_3=${BASH_REMATCH[3]}; tmp_4=${BASH_REMATCH[4]}
   chart["${tmp_2}"_"${tmp_3}"_"${tmp_1}"]=$tmp_4
        ;;
    esac

    [ $lastline = true ] && break
done <"$scorelog"

# ensure all active symbols were found in scorelog; deactivate not found
i=-1
while i=$((i+1)); [ "${sym_tag[$i]+x}" ]; do
    [ "${sym_active[$i]}" != true ] && continue
    j=-1; found=false
    while j=$((j+1)); [ "${ass_tag_name[$j]+x}" ]; do
   if [ "${sym_tag[$i]}" = "${ass_tag_name[$j]}" ]; then
       found=true
       break
   fi
    done
    if [ $found = false ]; then
   echo "Warning: tag ${sym_tag[$i]} not found in scorelog file." >&2
   sym_active[$i]=false
    fi
done

# charts webpage header
cat >"$pageout" <<-PAGEHEAD
<!DOCTYPE html>
<html><head>
    <title>Charts test</title>
    <meta content="text/html; charset=UTF-8" http-equiv="content-type">

    <!--[if lt IE 9]><script language="javascript" type="text/javascript"
      src="/jqplot/src/excanvas.js"></script><![endif]-->
    <script language="javascript" type="text/javascript"
      src="/jqplot/src/jquery.min.js"></script>
    <script language="javascript" type="text/javascript"
      src="/jqplot/src/jquery.jqplot.min.js"></script>
    <link rel="stylesheet" type="text/css"
      href="/jqplot/src/jquery.jqplot.css" />

    <script language="javascript" type="text/javascript"
      src="/jqplot/src/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script>
    <script language="javascript" type="text/javascript"
      src="/jqplot/src/plugins/jqplot.canvasTextRenderer.min.js"></script>

    <style>
      .button, .button:visited {
      display: inline-block;
      color: #ffffff;
      background: #222222;
      padding: 1px;
      margin-top: 10px;
      text-decoration:none;  /* remove link's underline */
      font-weight:bold;
      border:1px solid rgba(0,0,0,0.4);
      -moz-border-radius: 5px;
      -moz-box-shadow: 0px 0px 2px rgba(0,0,0,0.5);
      -webkit-border-radius: 5px;
      -webkit-box-shadow: 0px 0px 2px rgba(0,0,0,0.5);
      cursor: pointer;
      }

      .button:hover, .current {
      color: #ffffff !important;
      background: #662222 !important;
      }
    </style>

</head><body style="text-align:center; margin:0 auto;">
PAGEHEAD

# charts references & buttons
i=-1
while i=$((i+1)); [ "${sym_tag[$i]+x}" ]; do
    [ ${sym_active[$i]} = true ] || continue

      # jqPlot handle
    echo '<div id="chart_'$i'" style="width:690px; height:450px;"></div>'

    j=-1  # buttons
    while j=$((j+1)); [ "${sym_tag[$j]+x}" ]; do
   [ ${sym_active[$j]} = true ] || continue

   if [ $i -eq $j ]; then
        echo '<a class="button current" href="#chart_'$j'">'
   else echo '<a class="button"         href="#chart_'$j'">'
   fi
   echo          "${sym_button[$j]}"
   echo      '</a>'
    done
   
      # gap to following chart
    echo '<div style="height: 100px;"></div>'
done >>"$pageout"

# charts configuration
echo '<script id="source" language="javascript"' >>"$pageout"
echo '  type="text/javascript">'                 >>"$pageout"
i=-1
while i=$((i+1)); [ "${sym_tag[$i]+x}" ]; do
    [ ${sym_active[$i]} = true ] || continue

    j=-1  # take a local copy of the tag id
    while j=$((j+1)); [ "${ass_tag_id[$j]+x}" ]; do
   [ "${sym_tag[$i]}" != "${ass_tag_name[$j]}" ] && continue
   tag_id=${ass_tag_id[$j]}
    done

    case "${sym_tag[$i]}" in
    special_chart1)
   ;;
    special_chart2)
   ;;
    *)  # common charts
   echo '$.jqplot('  # jqplot function open

        # data series
   j=-1; jq_series='['
   while j=$((j+1)); [ "${ass_plr_id[$j]+x}" ]; do
       jq_series=${jq_series}'['
       for turn in $(eval echo {0..$totturns}); do
        a=${tag_id}; b=${ass_plr_id[$j]}
        jq_series=${jq_series}'['$turn,${chart["${a}"_"${b}"_${turn}]}'],'
        echo -n "$jq_series" >> matcharts.tmp && jq_series=""  # flush
       done
       # jq_series=${jq_series}'],'
       echo -n '],' >> matcharts.tmp
   done
   # jq_series=${jq_series}']'
   echo -n ']' >> matcharts.tmp #
   # echo \''chart_'$i\'','${jq_series}','
   echo \''chart_'$i\'','"$(< matcharts.tmp)"','
   rm matcharts.tmp

   echo '{'  # chart options start
   echo -n "title: '${sym_title[$i]}" '<br>'   # newline is not accepted
        echo -n '<span style="font-size: 0.7em;">'  #
   echo    "${sym_subtitle[$i]}" "</span>',"   #
   echo 'axes:{'
   delta=$(( (totturns / 20) + 5-(totturns / 20)%5 ))
   [ $((totturns % delta)) -eq 0 ] && k=0 || k=1
   k=1 # just vaffa
   min=0; max=$((totturns + k*10 - (totturns % 10)))
   [ $min -eq $max ] && max=$((min + 1))
   echo   'xaxis:{'
   echo     "min:${min}, max:${max}, tickInterval:${delta},"
   echo     "tickOptions: {formatString: '%d' }, label:'turns',"
   echo     'labelRenderer: $.jqplot.CanvasAxisLabelRenderer,'
   echo     'labelOptions: {'
   echo       'enableFontSupport: true,'
   echo       "fontFamily: 'sans-serif'"
   echo     '},'
   echo   '},'

        # determine vertical data limits
   _j=-1; min=0; max=0
   while _j=$((_j+1)); [ "${ass_plr_id[$_j]+x}" ]; do
       for _turn in $(eval echo {0..$totturns}); do
      y=${chart["${tag_id}"_"${ass_plr_id[$_j]}"_${_turn}]}
      [ "$y" ] || y=0  # no more data (user deleted from scorelog)
      [ $y -lt $min ] && min=$y
      [ $y -gt $max ] && max=$y
       done
   done
   [ $min -eq $max ] && max=$((min + 1))

   # determine vertical increment
   mag=1
   while mag=$((mag * 10)); do
       [ $((max - min)) -gt $mag ] && continue
       if [ $((max - min)) -lt $((mag / 5)) ]; then
       delta=$(( (mag / 10) / 10))
       else delta=$(( (mag / 10) ))
       fi
       [ $delta -eq 0 ] && delta=1  # happens if excursion < 10

         # ideal target is about 10-20 vertical divisions
       [ $(( (max - min) / delta )) -gt 25 ] && delta=$((delta * 2))
       [ $(( (max - min) / delta )) -lt 4 ]  && delta=$((delta / 5))
       [ $delta -eq 0 ] && delta=1
       [ $(( (max - min) / delta )) -lt 8 ]  && delta=$((delta / 2))
       break
   done
   [ $delta -eq 0 ] && delta=1

   # determine vertical chart limits
   if [ $min -ge 0 ]; then min=0
   else [ $((min % delta)) -eq 0 ] && k=0 || k=1
        min=$((min - k*delta - (min % delta)))
   fi
   if [ $max -le 0 ]; then max=0
   else [ $((max % delta)) -eq 0 ] && k=0 || k=1
        max=$((max + k*delta - (max % delta)))
   fi
   echo   'yaxis:{'
   echo     "min:${min}, max:${max}, tickInterval:${delta},"
   echo     "tickOptions: {formatString: '%d' },"
   echo     "label:'${sym_vlabel[$i]}',"
   echo     'labelRenderer: $.jqplot.CanvasAxisLabelRenderer,'
   echo     'labelOptions: {'
   echo       'enableFontSupport: true,'
   echo       "fontFamily: 'sans-serif'"
   echo     '},'
   echo   '},'
   echo '},'

   echo 'seriesDefaults: {'
   echo   'showMarker: false,'
   echo   'shadow: false,'
   echo '},'

   # temp legend
   echo "legend: {"
        echo   "show: true,"
        echo   "location: 'nw',"
        echo   "xoffset: 12,"
        echo   "yoffset: 12,"
        echo '},'

        # tmp legend labels
   echo 'series: ['
   j=-1
   while j=$((j+1)); [ "${ass_plr_name[$j]+x}" ]; do
       echo '{ label:' "'${ass_plr_name[$j]}'" '},'
   done
   echo '],'

   # temp players colors
          # (colors should be defined by some input at start)
   echo 'seriesColors: [ "#ee0000", "#007700", "#000000", "#ffff00",' \
             '"#8a2fe2", "#ff8c00", "#00ffff", "#8b4513","#d3d3d3","#ffd700",'\
             '"#ff1493", "#7cf200", "#da70d6", "#1e90ff", "#fa8077" ]'

   echo '}'  # chart options end
   echo ');' # jqplot function close
   ;;
    esac

done >>"$pageout"
echo '</script>' >>"$pageout"

echo '</body></html>' >>"$pageout"
User avatar
Davide
 
Posts: 864
Joined: Sat Mar 24, 2012 12:34 am

Return to Contribution

Who is online

Users browsing this forum: No registered users and 1 guest

cron