2024年8月29日 星期四

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h"
// 五行:
//               木2
//      水1           火3
//         金0     土4
//
// 先天八卦 vs 五行
//                         乾1->金0
//                兌2->金0          巽5->木2
//      離3->火3                              坎6->水1
//                震4->木2          艮7->土4
//                         坤8->土4
// const char *element5[5]   = {"金", "水", "木", "火", "土"};
const int  gossip8Mod5[9]  = {0, 0, 0, 3, 2, 2, 1, 4, 4};//(乾1,兌2)->金0, 離3->火3, (震4,巽5)->木2, 坎6->水1, (艮7,坤8)->土4
bool judgeResult(int change = 1, int same = 1) { // 用(變卦稱之用), 體(卦不變為體)
  int used = gossip8Mod5[change];// 轉五行 0 ~ 4 : "金", "水", "木", "火", "土"
  int body = gossip8Mod5[same];// 轉五行 0 ~ 4 : "金", "水", "木", "火", "土"
  if ((used + 4) % 5 == body || (used + 2) % 5 == body) return false;// 體 反生 用, 用 反剋 體 -> 凶
  return true; // 用 生 體, 體 剋 用, 用體 比 -> 吉  
}
int main() {
  int good = 0;
  for(int same = 1; same <= 8; same ++) { // 體 8 卦
    for(int change = 1; change <= 8; change ++) {   // 用 8 卦
      if (judgeResult(change, same)) {
        good ++;
        printf("吉 ");
      } else {
        printf("凶 ");
      }
    }
    printf("\n");
  }
  printf("吉/凶 = %d/%d\n", good, 64 - good);
  return 0;
}

2024年8月21日 星期三

使用 flutter 編寫簡單的萬年月曆

將以下程式碼複製貼到 main.dart 執行一遍

// main.dart

import 'package:flutter/material.dart';
// 判斷公(西)元是潤年的條件: (4 倍數且非 100 倍數) 或是 400 倍數
bool isLeapYear(int adYear) => (adYear % 4 == 0) && (adYear % 100 != 0) || (adYear % 400 == 0);

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override Widget build(BuildContext context) => MaterialApp(//    title: '月曆',
    debugShowCheckedModeBanner: false,
    theme: ThemeData.dark(),
    home: const Scaffold(body:  LunarCalender())        
  );
}

class LunarCalender extends StatefulWidget {
  const LunarCalender({super.key});
  @override State<LunarCalender> createState() => _LunarCalenderState();
}

class _LunarCalenderState extends State<LunarCalender> {
  final today = DateTime.now();
  final boxBorder = const BoxDecoration(border: Border(
       top: BorderSide(width: 1.0, color: Colors.grey),
      left: BorderSide(width: 1.0, color: Colors.grey),
     right: BorderSide(width: 1.0, color: Colors.grey),
    bottom: BorderSide(width: 1.0, color: Colors.grey),
  ));
  final cross = const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 7);
  //                            月份:  12,  1,    2,   3,   4,   5,   6,   7,   8,   9, 10, 11, 12 月
  List<int> daysofMonth = [31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];// 天數

  Widget monthView(int year, int month) { //todo: validate year and month
    daysofMonth[2] = isLeapYear(year) ? 29 : 28; //更新二月天數
    final y0 = year - 1;// 註: 公(西)元 1 年 1 月 1 日 是 星期日 (weekday = 0)
    int weekday = year + (y0 ~/ 4) + (y0 ~/ 400) - (y0 ~/ 100);// 潤年補天數
    for (int i = 1; i < month; i ++) { weekday += daysofMonth[i]; } // 直到上個月天數
    weekday %= 7;// 當月第 1 天的星期序, 0 是星期日, ..., 同時等於上個月尚需展示的天數
    final lastMonth = daysofMonth[month - 1];// 上個月天數
    final total = daysofMonth[month] + weekday;// 天數: 當月顯示天數 + 上個月在這個月顯示天數
    return GridView.builder(
      itemCount: total,
      padding: EdgeInsets.zero,
      gridDelegate: cross,
      itemBuilder: (BuildContext context, int i) {
        int atDay = i + 1 - weekday;// 擬顯示的日期, 位置同時與星期序相關
        int atMonth = month;// 擬顯示的月份
        if (atDay <= 0)  {// 修正為上個月的日期
          atDay += lastMonth;
          atMonth --;
        }
        return Container(
          decoration: boxBorder,
          child: Flex(
            direction: Axis.vertical,
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [Text("$atMonth 月"), Text("$atDay")]
          )
        );
      }
    );
  }

  @override Widget build(BuildContext context) {
    int year = today.year;
    int month = today.month;
    return Scaffold(
      appBar: AppBar(title: Center(child:Text("月曆 西元 $year 年 $month 月"))),
      body:  Flex(
        direction: Axis.vertical,
        children: [
          const Flex(
            direction: Axis.horizontal,
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Text('星期日'),
              Text('星期一'),
              Text('星期二'),
              Text('星期三'),
              Text('星期四'),
              Text('星期五'),
              Text('星期六')
            ]
          ),
          Expanded(child: monthView(year, month))
        ]
      )
    );    
  }
}

2024年8月16日 星期五

簡單學易經

我相信易經.
但它是一部教人作人處事的道理, 而不是用來預測未來的神秘絕學.
若以時間序來看古/今/後(過去, 現在, 未來)時, 易經描述的應該是:
        因過去採取作為, 現在所產生的後果.
而不是
        現在採取作為, 未來將會產生的後果.

因此若有人自稱是易經神算, 他百分之百是騙子, 如果他無條件利用易經原則, 教人作人處事, 那才稱的上是易經大師.

解卦者往往口條好, 能言善道, 博學多聞, 旁徵博引, 引經據典, 甚至還需加以穿鑿附會, 解卦過程不斷用修辭, 一點一點引領信徒到卦辭的關注點上.當預測準確實時, 口耳相傳就能引發廣大迴響, 解卦準的就被當作神算看待, 不准時默默無名無人理會, 無傷大雅, 自然也無礙事件的發展.這就像中樂透時, 得主會成為焦點, 但沒中的比比皆是, 無人聞問. 隨著時間久遠, 事實已不可考, 但保留下來的卦辭, 一些江湖術士拿來當作算命方程式, 利用信徒的奉為圭臬的易經 64 卦, 憑著三寸不爛之舌, 將信徒唬的一愣一愣, 分不清現實. 以古今預測今後, 的確是一個很合理的過程, 俗語說是三分天註定, 但更重要的是七分要靠努力. 用易經來占卜, 只是一個隨機過程, 占卜結果可以安定人心, 這才是學習這門課所必需保持的心態.

單從機率分佈來看, 占筮用的數字只有 4 種 {6,7,8,9}, 可以用 2 位元來代表所有數字, 接著要重複 6 次占出不同數字, 也就是總共產生出 6 * 2 = 12 位元的組合, 換句話說總樣本數只有 2¹² = 4096 個, 周易用了 64 卦, 而 4096 = 64 * 64, 也就是會呈現出多對一的映射(mapping)方式. 卦辭及爻辭是用來解釋卦的, 每個卦內含 6 個爻辭, 乾卦和坤卦另外有一個用詞, 因此共有 64 + 6 *  64 + 2 = 450 條解釋所有的卦象, 用現在計算機來算機率簡直輕而一舉. 但文字和語言的巧妙之處, 在於言者無心, 聽者有意之下, 甚至說者加以潤飾, 活得也能說成死的, 死的竟然可以說成活的, 所有解釋都變成可能, 機率僅僅變成是一個參考數據而已.


2024年8月13日 星期二

推算 24 節氣日期

用來推算未來 24 節氣日期, 雖然推算出來的詳細時間不準, 但日期很準, 若改用今年的小寒時間當基底, 推算未 50 或過去 50 年內的 24 節氣日期, 應該堪用.

#include <stdio.h>
#include <time.h>
const char *climate24_terms[24]  = {
    "小寒", "大寒", "立春", "雨水", "驚蟄", "春分",//: 節氣索引  5, 日時 = 夜時
    "清明", "穀雨", "立夏", "小滿", "芒種", "夏至",//: 節氣索引 11, 日時 > 夜時
    "小暑", "大暑", "立秋", "處暑", "白露", "秋分",//: 節氣索引 17, 日時 = 夜時
    "寒露", "霜降", "立冬", "小雪", "大雪", "冬至" //: 節氣索引 23, 日時 < 夜時
};//24節氣始於年初小寒,北半球:4(X)季始於立(X), 春分秋分日夜等長, 夏至日長夜短, 冬至夜長日短

bool is_leap_year(int ad_year) { // 公(西)元是潤年的條件: (4 倍數且非 100 倍數) 或是 400 倍數
    return (ad_year % 4 == 0) && (ad_year % 100 != 0) || (ad_year % 400 == 0);
}

int *find_climate24(int ad_year) {// 推算當年農曆 24 節氣的陽曆日期
    static const int table_2024[24][4] = {
        { 1,  6,  4, 49}, { 1, 20, 22,  7}, { 2,  4, 16, 27}, { 2, 19, 12, 13}, { 3,  5, 10, 23}, { 3, 20, 11,  6},// [ 5][]: 春分
        { 4,  4, 15,  2}, { 4, 19, 22,  0}, { 5,  5,  8, 10}, { 5, 20, 21,  0}, { 6,  5, 12, 10}, { 6, 21,  4, 51},// [11][]: 夏至
        { 7,  6, 22, 20}, { 7, 22, 15, 44}, { 8,  7,  8,  9}, { 8, 22, 22, 55}, { 9,  7, 11, 11}, { 9, 22, 20, 44},// [17][]: 秋分
        {10,  8,  3,  0}, {10, 23,  6, 15}, {11,  7,  6, 20}, {11, 22,  3, 56}, {12,  6, 23, 17}, {12, 21, 17, 21} // [23][]: 冬至
    }; // 2024 年 24 節氣的時間點 {月, 日, 時, 分} 參考資料 https://www.hko.gov.hk/tc/gts/astronomy/Solar_Term.htm
    static long climate24_table[24] = {0}; // 24 節氣時間曲線, 需製表一次
    static int days_of_solar_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    static int climate24_date[24 * 4]; // 回傳陣列 [月, 日, 時, 分, ...]
    if (climate24_table[0] == 0)  {
        days_of_solar_month[1] = is_leap_year(2024) ? 29 : 28;// 更新 2 月天數
        for (int i = 0; i < 24 ; i ++) {
            int month  = table_2024[i][0]; // 月
            long days  = table_2024[i][1]; // 日
            for (int j = 1; j < month; j ++) days += days_of_solar_month[j - 1]; // 計算總天數
            climate24_table[i] = days * 1440 + table_2024[i][2] * 60 + table_2024[i][3];// 分鐘
        }
    }
    days_of_solar_month[1] = is_leap_year(ad_year) ? 29 : 28;// 更新當年 2 月天數
    int *target = climate24_date; // 填入 [月, 日, 時, 分, ...]
    for (int n = 0; n < 24; n ++) {
        int &solar_month = target[0];
        int &solar_day     = target[1];
        int &solar_hour    = target[2];
        int &solar_min      = target[3];
        long dT = climate24_table[n];
        if (ad_year >= 2024) { // 推算未來
            for (int year = 2024; year < ad_year; year ++) {
                dT += (365.2421990741f - (is_leap_year(year) ? 366 : 365)) * 1440;
            }
        } else { // 回推過往
            for (int year = ad_year; year < 2024; year ++) {
                dT -= (365.2421990741f - (is_leap_year(year) ? 366 : 365)) * 1440;
            }
        }
        solar_day    = dT / 1440;// 日
        solar_hour  = dT % 1440 / 60; // 小時
        solar_min    = dT % 1440 % 60; // 分鐘
        solar_month = 1;// 日期從 1 月 1 日開始運算, 準備轉回當年日期
        for (int i = 1; i <= 12; i ++) { // 一年共 12 月
            int month_days = days_of_solar_month[solar_month - 1];
            if (solar_day <= month_days) break;
            solar_day -= month_days;
            solar_month ++;
        }
        target += 4; // 下一節氣
    }
    return climate24_date;
}


int main() {
    time_t now = time(0);// 現在時間
    struct tm *today = localtime(&now); // 取得今天的資料, todo: GMT + 8
    int ad_year = today->tm_year + 1900;// 今年: 西元年
    int ad_month = today->tm_mon + 1;   // 當月
    int ad_day = today->tm_mday;    
    printf("今天 西元%d 年 %d 月 %d 日\n\n", ad_year, ad_month, ad_day);
    ad_year -= 50;
    int *date = find_climate24(ad_year);
    for(int i = 0; i < 24; i++) {
        printf("%d 年 %s %2d 月 %2d 日 %2d:%2d\n", ad_year, climate24_terms[i], date[0], date[1], date[2], date[3]);
        date += 4;
    }    
}

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h" // 五行: //               木2 //      水1           火3 //         金0     土4 // // 先天八卦 vs 五行 //                    ...